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.Tank; 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 cpBody* tankBody; 37 cpBody* tankControlBody; 38 39 static void update(cpSpace* space, double dt) 40 { 41 // turn the control body_ based on the angle relative to the actual body_ 42 cpVect mouseDelta = cpvsub(ChipmunkDemoMouse, cpBodyGetPos(tankBody)); 43 cpFloat turn = cpvtoangle(cpvunrotate(cpBodyGetRot(tankBody), mouseDelta)); 44 cpBodySetAngle(tankControlBody, cpBodyGetAngle(tankBody) - turn); 45 46 // drive the tank towards the mouse 47 if (cpvnear(ChipmunkDemoMouse, cpBodyGetPos(tankBody), 30.0)) 48 { 49 cpBodySetVel(tankControlBody, cpvzero); // stop 50 } 51 else 52 { 53 cpFloat direction = (cpvdot(mouseDelta, cpBodyGetRot(tankBody)) > 0.0 ? 1.0 : -1.0); 54 cpBodySetVel(tankControlBody, cpvrotate(cpBodyGetRot(tankBody), cpv(30.0f * direction, 0.0f))); 55 } 56 57 cpSpaceStep(space, dt); 58 } 59 60 static cpBody* add_box(cpSpace* space, cpFloat size, cpFloat mass) 61 { 62 cpFloat radius = cpvlength(cpv(size, size)); 63 64 cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size))); 65 cpBodySetPos(body_, cpv(frand() * (640 - 2 * radius) - (320 - radius), frand() * (480 - 2 * radius) - (240 - radius))); 66 67 cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, size, size)); 68 cpShapeSetElasticity(shape, 0.0f); 69 cpShapeSetFriction(shape, 0.7f); 70 71 return body_; 72 } 73 74 static cpSpace* init() 75 { 76 ChipmunkDemoMessageString = "Use the mouse to drive the tank, it will follow the cursor.".dup; 77 78 cpSpace* space = cpSpaceNew(); 79 cpSpaceSetIterations(space, 10); 80 cpSpaceSetSleepTimeThreshold(space, 0.5f); 81 82 cpBody * staticBody = cpSpaceGetStaticBody(space); 83 cpShape* shape; 84 85 // Create segments around the edge of the screen. 86 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f)); 87 cpShapeSetElasticity(shape, 1.0f); 88 cpShapeSetFriction(shape, 1.0f); 89 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 90 91 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f)); 92 cpShapeSetElasticity(shape, 1.0f); 93 cpShapeSetFriction(shape, 1.0f); 94 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 95 96 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f)); 97 cpShapeSetElasticity(shape, 1.0f); 98 cpShapeSetFriction(shape, 1.0f); 99 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 100 101 shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f)); 102 cpShapeSetElasticity(shape, 1.0f); 103 cpShapeSetFriction(shape, 1.0f); 104 cpShapeSetLayers(shape, NOT_GRABABLE_MASK); 105 106 for (int i = 0; i < 50; i++) 107 { 108 cpBody* body_ = add_box(space, 20, 1); 109 110 cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body_, cpvzero, cpvzero)); 111 cpConstraintSetMaxBias(pivot, 0); // disable joint correction 112 cpConstraintSetMaxForce(pivot, 1000.0f); // emulate linear friction 113 114 cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body_, 0.0f, 1.0f)); 115 cpConstraintSetMaxBias(gear, 0); // disable joint correction 116 cpConstraintSetMaxForce(gear, 5000.0f); // emulate angular friction 117 } 118 119 // We joint the tank to the control body_ and control the tank indirectly by modifying the control body_. 120 tankControlBody = cpBodyNew(INFINITY, INFINITY); 121 tankBody = add_box(space, 30, 10); 122 123 cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero)); 124 cpConstraintSetMaxBias(pivot, 0); // disable joint correction 125 cpConstraintSetMaxForce(pivot, 10000.0f); // emulate linear friction 126 127 cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(tankControlBody, tankBody, 0.0f, 1.0f)); 128 cpConstraintSetErrorBias(gear, 0); // attempt to fully correct the joint each step 129 cpConstraintSetMaxBias(gear, 1.2f); // but limit it's angular correction rate 130 cpConstraintSetMaxForce(gear, 50000.0f); // emulate angular friction 131 132 return space; 133 } 134 135 static void destroy(cpSpace* space) 136 { 137 ChipmunkDemoFreeSpaceChildren(space); 138 cpBodyFree(tankControlBody); 139 cpSpaceFree(space); 140 } 141 142 ChipmunkDemo Tank = { 143 "Tank", 144 1.0 / 60.0, 145 &init, 146 &update, 147 &ChipmunkDemoDefaultDrawImpl, 148 &destroy, 149 };