1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | //1. Declare module name module ship; //2. The ship will need to make use of all these imports, plus we define playerShip class global import arc.window, arc.texture, arc.sound, arc.input, arc.scenegraph.all, arc.physics.shapes.circle, arc.types; import asteroid; import laserbolt; Ship playerShip; //3. Our ship class, it will be a 'scenegraph sprite' class Ship : arc.scenegraph.sprite.Sprite { public: //4. The max speed the ship can accelerate up to (pixels per ms) const float MAX_SPEED = 0.1; //5. This function is used by the physics system to tell if the ship collides with the asteroid void onCollideStart(Body a, Body b) { if(cast(Asteroid)b.getParent() !is null) { //6. If the ship does collide with the asteroid, we kill the ship kill(); } } //7. Initialize our ship this() { // A. The frame we define when the ship is not moving stillFrame = new Frame(Texture("astbin/ship/still.png")); // B. When the ship is moving, this moveAnimation is shown (checkout move.png to see how animation is set up) // the argument to linear animation are /animation/, /size of anim/, /numframes/, /time lapse between frames/, and /anim-loop style/ moveAnimation = new LinearAnimation(new Frame(Texture("astbin/ship/move.png"), Rect(32,32)), 3, 50, LinearAnimation.Direction.Circular); // C. When the ship dies, this explode animation is shown explosionAnimation = new LinearAnimation(new Frame(Texture("astbin/ship/explosion2.png"), Rect(32,32)), 7, 100); // D. sound effects explodeSnd = new Sound(new SoundFile("astbin/explosion.wav")); laserSnd = new Sound(new SoundFile("astbin/ship/laser.wav")); // E. Instead of calling process manually on sound effects, we can add them to an auto-process list // and arc.sound.process() will process these sounds automatically registerAutoProcessSound(explodeSnd); registerAutoProcessSound(laserSnd); // F. Circle is a physics class: radius is 13 and mass 100 physics = new Circle(13, 100); // G. Hook up our collision code to the physics circle physics.sigCollideStart.connect(&onCollideStart); // H. put ship at center of screen physics.translation = Point(arc.window.getWidth / 2, arc.window.getHeight / 2); // I. call the sprite constructor, give the transform node and the initial state super(physics, stillFrame); } //8. Unregister sounds from the auto-process list ~this() { unregisterAutoProcessSound(laserSnd); unregisterAutoProcessSound(explodeSnd); } //9. Reset the ship, this is usually done after the ship has been killed void reset() { // start out with the static animation state = stillFrame; // put at center of screen physics.translation.set(arc.window.getWidth / 2, arc.window.getHeight / 2); physics.velocity.set(0,0); physics.angularVelocity = 0; // ship is alive again alive = true; } //10. Accelerate by applying a forward force to the physics system void accelerateForward() { physics.force += Point.fromPolar(0.01, physics.rotation - PI/2); } //11. Decelerate void accelerateBackward() { physics.force += Point.fromPolar(0.01, physics.rotation + PI/2); } //12. rotational acceleration to the left void rotateLeft() { physics.angularVelocity = -0.003; } //13. rotational acceleration to the right void rotateRight() { physics.angularVelocity = 0.003; } //14. Process ship void process() { processControls(); } //15. Process the ships controls void processControls() { // start with no angular velocity physics.angularVelocity = 0; // keep the ship's speed from going above the max speed if(physics.velocity.lengthSquared > MAX_SPEED*MAX_SPEED) physics.velocity = physics.velocity.normaliseCopy() * MAX_SPEED; // process the ships controls if the ship is alive if(alive) { if (arc.input.keyDown(ARC_LEFT)) rotateLeft(); if (arc.input.keyDown(ARC_RIGHT)) rotateRight(); if (arc.input.keyPressed(ARC_UP) || arc.input.keyPressed(ARC_DOWN)) state = moveAnimation.start(); if (arc.input.keyUp(ARC_UP) && arc.input.keyUp(ARC_DOWN) && state !is stillFrame) state = stillFrame; // speedup when moving, slow down when not if (arc.input.keyDown(ARC_UP)) accelerateForward(); if (arc.input.keyDown(ARC_DOWN)) accelerateBackward(); // fire laser when control is hit if (arc.input.keyPressed(ARC_LCTRL)) { // play the laser sound laserSnd.play(); // add a new laser bolt to the scenegraph with specified position, angle, and rotation LaserBolt.addLaser( physics.translation + Point.fromPolar(32, physics.rotation - PI/2), physics.rotation ); } } } //16. Kill the ship and only if ship is alive void kill() { if(alive) { // play explode sound effect explodeSnd.stop(); explodeSnd.play(); // start the exploding animation state = explosionAnimation.start(); // ship is now dead alive = false; } } //17. Explode and Laser sound effects Sound explodeSnd, laserSnd; private: //18. Our ships variables, animation frames, physics body, and alive boolean Frame stillFrame; LinearAnimation explosionAnimation, moveAnimation; Body physics; bool alive=true; } |