1 /** 2 * The Render Engine 3 * CircleColliderComponent 4 * 5 * @fileoverview A collision component which determines collisions via 6 * bounding circle comparisons. 7 * 8 * @author: Brett Fattori (brettf@renderengine.com) 9 * 10 * @author: $Author: bfattori $ 11 * @version: $Revision: 1555 $ 12 * 13 * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com) 14 * 15 * Permission is hereby granted, free of charge, to any person obtaining a copy 16 * of this software and associated documentation files (the "Software"), to deal 17 * in the Software without restriction, including without limitation the rights 18 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 * copies of the Software, and to permit persons to whom the Software is 20 * furnished to do so, subject to the following conditions: 21 * 22 * The above copyright notice and this permission notice shall be included in 23 * all copies or substantial portions of the Software. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 * THE SOFTWARE. 32 * 33 */ 34 35 // The class this file defines and its required classes 36 R.Engine.define({ 37 "class":"R.components.collision.Circle", 38 "requires":[ 39 "R.components.Collider", 40 "R.math.Point2D", 41 "R.math.Vector2D", 42 "R.math.Rectangle2D", 43 "R.math.Circle2D", 44 "R.struct.CollisionData" 45 ] 46 }); 47 48 /** 49 * @class An extension of the {@link ColliderComponent} which will check if the 50 * object's are colliding based on their world bounding circles. If either of the 51 * objects does not have the {@link R.objects.Object2D#getWorldCircle} method 52 * the test will result in no collision. 53 * <p/> 54 * By default, this component will perform a simple intersection test which results 55 * in a simple <code>true</code> or <code>false</code> test. A more detailed test 56 * can be made by setting the component to perform a circle-to-circle collision test which 57 * will result in a collision data structure if a collision occurs. Setting the testing 58 * mode is done by calling the {@link #setTestMode} method. 59 * 60 * @param name {String} Name of the component 61 * @param collisionModel {SpatialCollection} The collision model 62 * @param priority {Number} Between 0.0 and 1.0, with 1.0 being highest 63 * 64 * @extends R.components.Collider 65 * @constructor 66 * @description Creates a collider component for circle-circle collision testing. Each object 67 * must implement either the {@link R.objects.Object2D#getWorldBox} or 68 * {@link R.objects.Object2D#getCircle} method and return a world-oriented bounding box or 69 * circle, respectively. 70 */ 71 R.components.collision.Circle = function () { 72 "use strict"; 73 return R.components.Collider.extend(/** @scope R.components.collision.Circle.prototype */{ 74 75 hasMethods:null, 76 cData:null, 77 78 /** 79 * Releases the component back into the pool for reuse. See {@link R.engine.PooledObject#release} 80 * for more information. 81 */ 82 release:function () { 83 this.base(); 84 this.hasMethod = null; 85 this.cData = null; 86 }, 87 88 /** 89 * Deprecated in favor of {@link #setGameObject} 90 * @deprecated 91 */ 92 setHostObject:function (gameObject) { 93 this.setGameObject(gameObject); 94 }, 95 96 /** 97 * Establishes the link between this component and its game object. 98 * When you assign components to a game object, it will call this method 99 * so that each component can refer to its game object, the same way 100 * a game object can refer to a component with {@link R.engine.GameObject#getComponent}. 101 * 102 * @param gameObject {R.engine.GameObject} The object which hosts this component 103 */ 104 setGameObject:function (gameObject) { 105 this.base(gameObject); 106 this.hasMethods = [gameObject.getWorldCircle != undefined]; // getWorldBox 107 /* pragma:DEBUG_START */ 108 // Test if the host has getWorldCircle 109 AssertWarn(this.hasMethods[0], "Object " + gameObject.toString() + " does not have getWorldCircle() method"); 110 /* pragma:DEBUG_END */ 111 }, 112 113 /** 114 * If a collision occurs, calls the game object's <tt>onCollide()</tt> method, 115 * passing the time of the collision, the potential collision object, and the game object 116 * and target's masks. The return value should either tell the collision tests to continue or stop. 117 * 118 * @param time {Number} The engine time (in milliseconds) when the potential collision occurred 119 * @param dt {Number} The delta between the world time and the last time the world was updated 120 * in milliseconds. 121 * @param collisionObj {R.engine.GameObject} The game object with which the collision potentially occurs 122 * @param objectMask {Number} The collision mask for the game object 123 * @param targetMask {Number} The collision mask for <tt>collisionObj</tt> 124 * @return {Number} A status indicating whether to continue checking, or to stop 125 */ 126 testCollision:function (time, dt, collisionObj, objectMask, targetMask) { 127 if (this.getCollisionData() != null) { 128 // Clean up old data first 129 this.getCollisionData().destroy(); 130 this.setCollisionData(null); 131 } 132 133 // Early out if no method(s) 134 if (!this.hasMethods[0] && !collisionObj.getWorldCircle) { 135 return R.components.Collider.CONTINUE; // Can't perform test 136 } 137 138 // See if a collision will occur 139 var linked = this.getLinkedBody(), 140 host = this.getGameObject(), 141 circle1 = host.getWorldCircle(), 142 circle2 = collisionObj.getWorldCircle(); 143 144 if (this.getTestMode() == R.components.Collider.SIMPLE_TEST && 145 circle1.isIntersecting(circle2)) { 146 147 // Intersection test passed 148 return this.base(time, dt, collisionObj, objectMask, targetMask); 149 150 } else { 151 var tRad = circle1.getRadius() + circle2.getRadius(), 152 c1 = circle1.getCenter(), c2 = circle2.getCenter(); 153 154 var distSqr = (c1.x - c2.x) * (c1.x - c2.x) + 155 (c1.y - c2.y) * (c1.y - c2.y); 156 157 if (distSqr < (tRad * tRad)) { 158 // Collision occurred, how much to separate circle1 from circle2 159 var diff = tRad - Math.sqrt(distSqr); 160 161 // If we got here, there is a collision 162 var sep = R.math.Vector2D.create((c2.x - c1.x) * diff, (c2.y - c1.y) * diff); 163 this.setCollisionData(R.struct.CollisionData.create(sep.len(), 164 R.math.Vector2D.create(c2.x - c1.x, c2.y - c1.y).normalize(), 165 host, 166 collisionObj, 167 sep, 168 time, 169 dt)); 170 171 return this.base(time, collisionObj, objectMask, targetMask); 172 } 173 } 174 175 // No collision 176 return R.components.Collider.CONTINUE; 177 } 178 179 /* pragma:DEBUG_START */, execute:function (renderContext, time, dt) { 180 this.base(renderContext, time, dt); 181 // Debug the collision box 182 if (R.Engine.getDebugMode() && !this.isDestroyed()) { 183 var linked = this.getLinkedBody(), 184 origin = R.math.Point2D.create(linked ? linked.getLocalOrigin() : R.math.Point2D.ZERO), 185 rect = R.math.Rectangle2D.create(this.getGameObject().getWorldBox()); 186 187 rect.offset(origin.neg()); 188 189 renderContext.postRender(function () { 190 this.setLineStyle("yellow"); 191 this.setLineWidth(1); 192 this.drawRectangle(rect); 193 }); 194 195 origin.destroy(); 196 } 197 } 198 /* pragma:DEBUG_END */ 199 200 }, { /** @scope R.components.collision.Circle.prototype */ 201 202 /** 203 * Get the class name of this object 204 * 205 * @return {String} The string "R.components.collision.Circle" 206 */ 207 getClassName:function () { 208 return "R.components.collision.Circle"; 209 } 210 211 }); 212 };