====== Coastaldefence ======
**Group 12**
^ Group Member ^ Main Responsibilities ^
| Jussi Laakkonen (0237801) | All and everything |
====== Idea ======
My idea is to improve the old battleship game with:
* Multiple actions (3) per turn
* Shooting
* Moving (not impl)
* Repairing (not impl)
* Ships have different amount of damage
* Ships have different weapons that make different amounts of damage
* Different type ammo travel with different speeds (map unit/turn)
* Ships can move to another players territory where the ships are visible (not impl)
* The speed is different for each ship (map unit/turn)
* Network support (not impl - done as hotseat game)
The screen size is enough for this kind of small strategy game, so it is doable. The screen is divided into 60x60 blocks, i.e. 9x16.
The game includes open data by using a nearby coastline retrieved by the user GPS coordinates from a map service - skipped, cannot pull this alone SINCE ERNO ABANONDED ME!
====== Timetable ======
^ Day ^ What to do ^
^ Monday| Helping others, try the SDK |
^ Tuesday| More OS/SDK issue fixing and support at codecamp, QML reading |
^ Wednesday| Coding, C++ and QML integration |
^ Thurday| Coding, QML, QML, QML and a bit C++ to prevent insanity |
^ Friday| A brief demo of the app and reset of brains afterwards |
====== Motivation ======
I just had a idea to improve the old battleships game with multiple additional features and, since I got my own Jolla phone I decided to try a little bit of Qt + QML coding at the codecamp and skip all other research for a week. QML was completely new for me but I learnt a lot from it during the short stint - and keep on learning atm.
====== Features ======
Implemented features:
===== Technical =====
* Multiple views (start screen, ship placing mode, turn changes, shooting mode, game ending) for both players
* Delayed view changes with (using a timer)
changeplayertimer = true // Starts the timer
...
Timer {
id: changeplayertimer
interval: timerdelay // In ms
running: false
repeat: false
onTriggered: {
if(pageStack.busy === false) // The app will crash if pagestack is manipulated within page transfer
pageStack.clear() // Clear stack
pageStack.push(Qt.resolvedUrl("PlayerTurn.qml")) // Load new page as only page in the stack
// The gamedata is retrieved from C++ engine, so when turn changes, the main page will be set with another players data
}
}
}
}
* C++ Engine for the game
* Define functions to be used in QML
Q_INVOKABLE static GameEngine* initEngine();
Q_INVOKABLE qint16 getIActivePlayer();
* Use in the main cpp-file in order to utilize the object reference in QML
int main(int argc, char *argv[])
{
QScopedPointer app(SailfishApp::application(argc, argv));
QScopedPointer view(SailfishApp::createView());
GameEngine* engine = GameEngine::initEngine(); // A singleton instance
view->rootContext()->setContextProperty("GameEngine",engine); // Can be used as "GameEngine" in QML
view->setSource(SailfishApp::pathTo("qml/coastaldefence.qml"));
view->show();
return app->exec();
}
* Use the object in QMLinfotext.text = "Player" + GameEngine.getIActivePlayer() + " at game state: " + GameEngine.getGameState()
* QML specific
* Moving of image objects on the screen
Grid { // Place items on a grid with specific column and row counts - the background grid for selecting coordinate (a rectangle)
id: maingrid
columns: GameEngine.gameAreaX()
rows: GameEngine.gameAreaY()
spacing: 0
Repeater {
id: elements
model: GameEngine.gameAreaX()*GameEngine.gameAreaY() // The amount of elements
delegate: Placeblock { // A element (rectangle of 60x60, defined in "Placeblock.qml", each gets unique "index", an index number in the grid
// x and y coordinates can be calculated from these
}
}
Item {
Repeater { // The ships
id: ships
model: page.shiplist // A property string list for elements
delegate: Ship {
name: page.shiplist[index] // Set name for the ship using index to list
}
}
}
// From Ship.qml
Rectangle {
MouseArea {
onClicked: {
if(GameEngine.getGameState() === 2 ) {
if(GameEngine.placeShip(page.activename,xcoordinate,0,ycoordinate,0) === 0) {
page.active = parent // Set the active selection in the main "page"
page.activename = parent.name // And the name
}
}
}
// From Placingblock.qml
Rectangle {
property int xcoordinate: index % GameEngine.gameAreaX() // x coordinate inside the game engine - not gui x
property int ycoordinate: Math.floor(index / GameEngine.gameAreaX())
...
MouseArea {
onClicked: {
if(GameEngine.getGameState() === 2 ) {
if(GameEngine.placeShip(page.activename,xcoordinate,0,ycoordinate,0) === 0) {
page.active.x = parent.x // Set the corner of the active (the ship in main gui) to be the same as this rectangle in the grid
page.active.y = parent.y
}
}
}
}
===== The game =====
==== Game Area ====
* 9x16 grid of 60x60 blocks where the oil of the enemy will be shed
* Initially 9x48 where each player has a "safe harbour" of 9x16 with fog-of-war from the halfway to the enemy harbour <- not impl. due lack of time
* Enemy ships would be seen if moved past the halfway
* Two player **hotseat game**, game notifies with a turn change and requests interaction
* When turn changes game reloads the placing/shooting view with new data after user has clicked the button on turn change screen
==== Ships ====
* Each player has 3 ships with different amounts of armor (if armor gets below 0 the ship is destroyed and cannot shoot anymore) and a different weapon set
^ Type ^ length ^ armor ^ weapons ^
| Submarine | 2 | 150 | torpedo and coastal gun |
| Battleship | 3 | 170 | weapons: machine gun and deck cannon |
| Cruiser | 4 | 200 | weapons: torpedo and deck cannon|
* Each player first selects positions for the ships
* Ships cannot go outside the screen
* Ships can be rotated - not impl - not enough time during cc
* Ships cannot overlap each other
==== Shooting ====
* On each turn player can select a ship which to use and to shoot the player must select a weapon
* Different weapons (2 weapons/ship) do different amount of damage and have different traverse speeds (speed skipped due lack of time)
^ Type ^ Damage ^
| Coastal gun | 50 |
| Deck cannon | 25 |
| Torpedo | 40 |
| Machine gun | 10 |
* Player has to select which weapon to use (the selection menu shows on top of the ship - which is too small, will be fixed in the future development version) and then press the S-box to go to shooting mode.
* When shooting player's view is changed to shooting view that shows the enemy harbor (coastline)
* At the enemy harbour view the player can select a coordinate which to shoot. Previous shots are shown as (also on own harbour view):
* Red: There was something player hit
* Green: Miss
* Black: Destroyed a ship that occupied the coordinate
* Player can shoot multiple times into one position, hence the amount of armor on the ships (this is not)
* Player can also cancel the shooting by selecting previous page from the page stack
* Player can cancel the turn by selecting the item from the pulldown menu
==== Info and ending ====
* During gameplay, player gets some information on the screen about the selected ship (name, armor, weapons) - a crude text-element. The player is also informed at the shooting screen about the shot result and is then transferred back to a) own harbour (navigate back) b) player change screen (clear pagestack and push anew page) depending on the game state
* The game ends when other player has destroyed all of the enemy ships and winner is shown on separate screen (game cannot be closed using the button - don't know how to do it yet)
====== Tech ======
Sailfish SDK
Qt C++ (game engine)
QML (gui)
GIMP (editing the one ship image, retrieved from www.the-blueprints.com)
====== Screenshots ======
===== New game and placing mode =====
{{:jolla2014:group12:1.png?200|}}
{{:jolla2014:group12:2.png?200|}}
{{:jolla2014:group12:3.png?200|}}
{{:jolla2014:group12:4.png.png?200|}}
{{:jolla2014:group12:5.png?200|}}
{{:jolla2014:group12:6.png?200|}}
===== Shooting mode =====
{{:jolla2014:group12:7.png?200|}}
{{:jolla2014:group12:8.png?200|}}
{{:jolla2014:group12:9.png?200|}}
{{:jolla2014:group12:10.png?200|}}
{{:jolla2014:group12:11.png?200|}}
{{:jolla2014:group12:12.png?200|}}
===== End times =====
{{:jolla2014:group12:13.png?200|}}
{{:jolla2014:group12:14.png?200|}}
====== Software Poster ======
{{:jolla2014:group12:poster.png?300|}}
====== Project report ======
TBD
====== Source ======
{{:jolla2014:group12:coastaldefence.tar.gz|Sources as tar.gz}}
{{:jolla2014:group12:coastaldefence-0.1-1.armv7hl.rpm|rpm for Jolla}}
====== Comments ======
The usage of C++ in QML was eventually easy but my way may not be the best. It is also possible to use QObjects in the QML and then you should not need to use one big instance that you call everytime. When the object is inherited from QObject type it is possible to, e.g.: property QObject object: GameEngine.getShip("cruiser")
object = GameEngine.getShip("sub")
Do not pass 8bit integers from Qt C++ to QML - it does not seem to be counting the bits correctly and the 8bit values are shown as negative integers in QML. Might be an issue with signed/unsigned - didn't have time to look this further and replaced every 8bit integer with 16bits, which were handled correctly.
Element visibility is a nice feature that every QML object seems to have, it is not putting itself on top of any other object while hidden but it is completely untouchable - I will use this in future version of the game to give a full screen popup when a ship is clicked. The popup will give detailed information about the ship: name, history, weapons, ammo count, speed, size, strength (armor), etc. For example:
Rectangle {
id: box1
visible: GameEngine.getGameState() === 0 ? false : true // Would set the box visible only when game is not in state 0
}