1 /* 2 * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 module demo.Player; 23 24 import core.stdc.stdlib; 25 26 import std.math; 27 28 alias M_PI_2 = PI_2; 29 30 import demo.dchip; 31 32 import demo.ChipmunkDebugDraw; 33 import demo.ChipmunkDemo; 34 import demo.types; 35 36 enum PLAYER_VELOCITY = 500.0; 37 38 enum PLAYER_GROUND_ACCEL_TIME = 0.1; 39 enum PLAYER_GROUND_ACCEL = (PLAYER_VELOCITY / PLAYER_GROUND_ACCEL_TIME); 40 41 enum PLAYER_AIR_ACCEL_TIME = 0.25; 42 enum PLAYER_AIR_ACCEL = (PLAYER_VELOCITY / PLAYER_AIR_ACCEL_TIME); 43 44 enum JUMP_HEIGHT = 50.0; 45 enum JUMP_BOOST_HEIGHT = 55.0; 46 enum FALL_VELOCITY = 900.0; 47 enum GRAVITY = 2000.0; 48 49 static cpBody * playerBody = null; 50 static cpShape* playerShape = null; 51 52 static cpFloat remainingBoost = 0; 53 static cpBool grounded = cpFalse; 54 static cpBool lastJumpState = cpFalse; 55 56 static void SelectPlayerGroundNormal(cpBody* body_, cpArbiter* arb, cpVect* groundNormal) 57 { 58 cpVect n = cpvneg(cpArbiterGetNormal(arb, 0)); 59 60 if (n.y > groundNormal.y) 61 { 62 (*groundNormal) = n; 63 } 64 } 65 66 static void playerUpdateVelocity(cpBody* body_, cpVect gravity, cpFloat damping, cpFloat dt) 67 { 68 int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); 69 70 // Grab the grounding normal from last frame 71 cpVect groundNormal = cpvzero; 72 cpBodyEachArbiter(playerBody, safeCast!cpBodyArbiterIteratorFunc(&SelectPlayerGroundNormal), &groundNormal); 73 74 grounded = (groundNormal.y > 0.0); 75 76 if (groundNormal.y < 0.0f) 77 remainingBoost = 0.0f; 78 79 // Do a normal-ish update 80 cpBool boost = (jumpState && remainingBoost > 0.0f); 81 cpVect g = (boost ? cpvzero : gravity); 82 cpBodyUpdateVelocity(body_, g, damping, dt); 83 84 // Target horizontal speed for air/ground control 85 cpFloat target_vx = PLAYER_VELOCITY * ChipmunkDemoKeyboard.x; 86 87 // Update the surface velocity and friction 88 cpVect surface_v = cpv(target_vx, 0.0); 89 playerShape.surface_v = surface_v; 90 playerShape.u = (grounded ? PLAYER_GROUND_ACCEL / GRAVITY : 0.0); 91 92 // Apply air control if not grounded 93 if (!grounded) 94 { 95 // Smoothly accelerate the velocity 96 playerBody.v.x = cpflerpconst(playerBody.v.x, target_vx, PLAYER_AIR_ACCEL * dt); 97 } 98 99 body_.v.y = cpfclamp(body_.v.y, -FALL_VELOCITY, INFINITY); 100 } 101 102 static void update(cpSpace* space, double dt) 103 { 104 int jumpState = (ChipmunkDemoKeyboard.y > 0.0f); 105 106 // If the jump key was just pressed this frame, jump! 107 if (jumpState && !lastJumpState && grounded) 108 { 109 cpFloat jump_v = cpfsqrt(2.0 * JUMP_HEIGHT * GRAVITY); 110 playerBody.v = cpvadd(playerBody.v, cpv(0.0, jump_v)); 111 112 remainingBoost = JUMP_BOOST_HEIGHT / jump_v; 113 } 114 115 // Step the space 116 cpSpaceStep(space, dt); 117 118 remainingBoost -= dt; 119 lastJumpState = cast(bool)jumpState; 120 } 121 122 static cpSpace* init() 123 { 124 cpSpace* space = cpSpaceNew(); 125 space.iterations = 10; 126 space.gravity = cpv(0, -GRAVITY); 127 128 // space.sleepTimeThreshold = 1000; 129 space.enableContactGraph = cpTrue; 130 131 cpBody * body_; 132 cpBody * staticBody = space.staticBody; 133 cpShape* shape; 134 135 // Create segments around the edge of the screen. 136 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); 137 shape.e = 1.0f; 138 shape.u = 1.0f; 139 shape.layers = NOT_GRABABLE_MASK; 140 141 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); 142 shape.e = 1.0f; 143 shape.u = 1.0f; 144 shape.layers = NOT_GRABABLE_MASK; 145 146 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); 147 shape.e = 1.0f; 148 shape.u = 1.0f; 149 shape.layers = NOT_GRABABLE_MASK; 150 151 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); 152 shape.e = 1.0f; 153 shape.u = 1.0f; 154 shape.layers = NOT_GRABABLE_MASK; 155 156 // Set up the player 157 body_ = cpSpaceAddBody(space, cpBodyNew(1.0f, INFINITY)); 158 body_.p = cpv(0, -200); 159 body_.velocity_func = &playerUpdateVelocity; 160 playerBody = body_; 161 162 shape = cpSpaceAddShape(space, cpBoxShapeNew3(body_, cpBBNew(-15.0, -27.5, 15.0, 27.5), 10.0)); 163 164 // shape = cpSpaceAddShape(space, cpSegmentShapeNew(playerBody, cpvzero, cpv(0, radius), radius)); 165 shape.e = 0.0f; 166 shape.u = 0.0f; 167 shape.collision_type = 1; 168 playerShape = shape; 169 170 // Add some boxes to jump on 171 for (int i = 0; i < 6; i++) 172 { 173 for (int j = 0; j < 3; j++) 174 { 175 body_ = cpSpaceAddBody(space, cpBodyNew(4.0f, INFINITY)); 176 body_.p = cpv(100 + j * 60, -200 + i * 60); 177 178 shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, 50, 50)); 179 shape.e = 0.0f; 180 shape.u = 0.7f; 181 } 182 } 183 184 return space; 185 } 186 187 static void destroy(cpSpace* space) 188 { 189 ChipmunkDemoFreeSpaceChildren(space); 190 cpSpaceFree(space); 191 } 192 193 ChipmunkDemo Player = { 194 "Platformer Player Controls", 195 1.0 / 180.0, 196 &init, 197 &update, 198 &ChipmunkDemoDefaultDrawImpl, 199 &destroy, 200 };