1     /**
2         A class to represent the player on the screen
3         @author <a href="mailto:matthewcasperson@gmail.com">Matthew Casperson</a>
4         @class
5     */
6     function Player()
7 {
8 /** The maximum height of the jump 9 @type Number 10 */ 11 this.jumpHeight = 64;
12 /** The constant or half PI 13 @type Number 14 */ 15 this.halfPI = Math.PI / 2;
16 /** The amount of time to spend in the air when jumping 17 @type Number 18 */ 19 this.jumpHangTime = 0.5;
20 /** The speed to progress alone the sine wave that defines 21 the jumping arc 22 @type Number 23 */ 24 this.jumpSinWaveSpeed = this.halfPI / this.jumpHangTime;
25 /** The current position on the sine wave that defines the jump arc 26 @type Number 27 */ 28 this.jumpSinWavePos = 0;
29 /** The rate to fall at 30 @type Number 31 */ 32 this.fallMultiplyer = 1.5;
33 /** True when the player is on the ground, false otherwise 34 @type Boolean 35 */ 36 this.grounded = true;
37 /** the players running speed 38 @type Number 39 */ 40 this.speed = 75;
41 /** True if the player is moving left, false otherwise 42 @type Boolean 43 */ 44 this.left = false;
45 /** True if the player is moving right, false otherwise 46 @type Boolean 47 */ 48 this.right = false;
49 /** A reference to the level object 50 @type Level 51 */ 52 this.level = null;
53 /** The distance between the player and the edge of the screen 54 @type Number 55 */ 56 this.screenBorder = 20;
57 58 /** 59 Initialises this object 60 */ 61 this.startupPlayer = function(level)
62 {
63 this.startupAnimatedGameObject(g_idle_left, 300, 400 - 48 - 48, 4, 6, 20);
64 this.level = level;
65 return this;
66 }
67 68 /** 69 Called when a key is pressed 70 @param event Event Object 71 */ 72 this.keyDown = function(event)
73 {
74 var updateRequired = false;
75 76 // left 77 if (event.keyCode == 37 && !this.left)
78 {
79 this.left = true;
80 updateRequired = true;
81 }
82 // right 83 if (event.keyCode == 39 && !this.right)
84 {
85 this.right = true;
86 updateRequired = true;
87 }
88 if (event.keyCode == 32 && this.grounded)
89 {
90 this.grounded = false;
91 this.jumpSinWavePos = 0;
92 }
93 94 if (updateRequired)
95 this.updateAnimation();
96 97 }
98 99 /** 100 Called when a key is pressed 101 @param event Event Object 102 */ 103 this.keyUp = function(event)
104 {
105 // left 106 if (event.keyCode == 37)
107 {
108 this.left = false;
109 this.setAnimation(g_idle_left, 6, 20);
110 }
111 // right 112 if (event.keyCode == 39)
113 {
114 this.right = false;
115 this.setAnimation(g_idle_right, 6, 20);
116 }
117 118 this.updateAnimation();
119 }
120 121 /** 122 Updates the current animation depending on the movement 123 of the player. This accounts for the fact that both 124 the left and right arrow keys can be pressed at the 125 same time. 126 */ 127 this.updateAnimation = function()
128 {
129 if (this.right && this.left)
130 this.setAnimation(g_idle_left, 6, 20);
131 else if (this.right)
132 this.setAnimation(g_run_right, 12, 20);
133 else if (this.left)
134 this.setAnimation(g_run_left, 12, 20);
135 }
136 137 /** 138 Updates the object 139 @param dt The time since the last frame in seconds 140 @param context The drawing context 141 @param xScroll The global scrolling value of the x axis 142 @param yScroll The global scrolling value of the y axis 143 */ 144 this.update = function (/**Number*/ dt, /**CanvasRenderingContext2D*/context, /**Number*/ xScroll, /**Number*/ yScroll)
145 {
146 if (this.left)
147 this.x -= this.speed * dt;
148 if (this.right)
149 this.x += this.speed * dt;
150 151 // XOR operation (JavaScript does not have a native XOR operator) 152 // only test for a collision if the player is moving left or right (and not trying to do both at 153 // the same time) 154 if ((this.right || this.left) && !(this.left && this.right))
155 {
156 // this will be true until the player is no longer colliding 157 var collision = false;
158 // the player may have to be pushed back through several block stacks (especially if the 159 // frame rate is very slow) 160 do 161 {
162 // the current position of the player (test the left side if running left 163 // and the right side if running right) 164 var xPos = this.left ? this.x : this.x + this.frameWidth;
165 // the index of stack of blocks that the player is standing on/in 166 var currentBlock = this.level.currentBlock(xPos);
167 // the height of the stack of blocks that the player is standing on/in 168 var groundHeight = this.level.groundHeight(currentBlock);
169 // the height of the player (we need the height from the ground up, 170 // whereas the this.y value represents the position of the player 171 // from the "sky" down). 172 var playerHeight = context.canvas.height - (this.y + this.image.height);
173 // if the player is not higher than the stack of blocks, it must be colliding 174 if (playerHeight < groundHeight)
175 {
176 collision = true;
177 // we are moving right, so push the player left 178 if (this.right)
179 this.x = this.level.blockWidth * currentBlock - this.frameWidth - 1;
180 // we are moving left, push the player right 181 else 182 this.x = this.level.blockWidth * (currentBlock + 1);
183 }
184 else 185 {
186 collision = false;
187 }
188 } while (collision)
189 }
190 191 // keep the player bound to the level 192 if (this.x > this.level.blocks.length * this.level.blockWidth - this.frameWidth - 1)
193 this.x = this.level.blocks.length * this.level.blockWidth - this.frameWidth - 1;
194 if (this.x > context.canvas.width - this.frameWidth + xScroll - this.screenBorder)
195 g_GameObjectManager.xScroll = this.x - (context.canvas.width - this.frameWidth - this.screenBorder);
196 // modify the xScroll value to keep the player on the screen 197 if (this.x < 0)
198 this.x = 0;
199 if (this.x - this.screenBorder < xScroll)
200 g_GameObjectManager.xScroll = this.x - this.screenBorder;
201 202 // if the player is jumping or falling, move along the sine wave 203 if (!this.grounded)
204 {
205 // the last position on the sine wave 206 var lastHeight = this.jumpSinWavePos;
207 // the new position on the sine wave 208 this.jumpSinWavePos += this.jumpSinWaveSpeed * dt;
209 210 // we have fallen off the bottom of the sine wave, so continue falling 211 // at a predetermined speed 212 if (this.jumpSinWavePos >= Math.PI)
213 this.y += this.jumpHeight / this.jumpHangTime * this.fallMultiplyer * dt;
214 // otherwise move along the sine wave 215 else 216 this.y -= (Math.sin(this.jumpSinWavePos) - Math.sin(lastHeight)) * this.jumpHeight;
217 }
218 219 // now that the player has had it's y position changed we need to check for a collision 220 // with the ground below the player. we have to check both the players left and right sides 221 // for a collision with the ground 222 223 // left side 224 var currentBlock1 = this.level.currentBlock(this.x);
225 // right side 226 var currentBlock2 = this.level.currentBlock(this.x + this.frameWidth);
227 // ground height below the left side 228 var groundHeight1 = this.level.groundHeight(currentBlock1);
229 // ground height below the right side 230 var groundHeight2 = this.level.groundHeight(currentBlock2);
231 // the heighest point under the player 232 var maxGroundHeight = groundHeight1 > groundHeight2 ? groundHeight1 : groundHeight2;
233 // the players height (relaitive to the bottom of the screen) 234 var playerHeight = context.canvas.height - (this.y + this.image.height);
235 236 // we have hit the ground 237 if (maxGroundHeight >= playerHeight)
238 {
239 this.y = context.canvas.height - maxGroundHeight - this.image.height;
240 this.grounded = true;
241 this.jumpSinWavePos = 0;
242 }
243 // otherwise we are falling 244 else if (this.grounded)
245 {
246 this.grounded = false;
247 // starting falling down the sine wave (i.e. from the top) 248 this.jumpSinWavePos = this.halfPI;
249 }
250 }
251 }
252 253 Player.prototype = new AnimatedGameObject;
Top