1cfd74d65d832137e20e193c960802afba73b5d38sm/* 23c1e67e433728684b5f228c5d4f3e5b1457bb271sm * Copyright (C) 2010 The Android Open Source Project 3cfd74d65d832137e20e193c960802afba73b5d38sm * 4cfd74d65d832137e20e193c960802afba73b5d38sm * Licensed under the Apache License, Version 2.0 (the "License"); 5cfd74d65d832137e20e193c960802afba73b5d38sm * you may not use this file except in compliance with the License. 6cfd74d65d832137e20e193c960802afba73b5d38sm * You may obtain a copy of the License at 7cfd74d65d832137e20e193c960802afba73b5d38sm * 8cfd74d65d832137e20e193c960802afba73b5d38sm * http://www.apache.org/licenses/LICENSE-2.0 9cfd74d65d832137e20e193c960802afba73b5d38sm * 10cfd74d65d832137e20e193c960802afba73b5d38sm * Unless required by applicable law or agreed to in writing, software 11cfd74d65d832137e20e193c960802afba73b5d38sm * distributed under the License is distributed on an "AS IS" BASIS, 12cfd74d65d832137e20e193c960802afba73b5d38sm * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cfd74d65d832137e20e193c960802afba73b5d38sm * See the License for the specific language governing permissions and 14cfd74d65d832137e20e193c960802afba73b5d38sm * limitations under the License. 15cfd74d65d832137e20e193c960802afba73b5d38sm */ 16cfd74d65d832137e20e193c960802afba73b5d38sm 17cfd74d65d832137e20e193c960802afba73b5d38sm 18cfd74d65d832137e20e193c960802afba73b5d38smpackage com.replica.replicaisland; 19cfd74d65d832137e20e193c960802afba73b5d38sm 20cfd74d65d832137e20e193c960802afba73b5d38smimport java.util.Comparator; 21cfd74d65d832137e20e193c960802afba73b5d38sm 22cfd74d65d832137e20e193c960802afba73b5d38sm/** 23cfd74d65d832137e20e193c960802afba73b5d38sm * Handles collision against the background. Snaps colliding objects out of collision and reports 24cfd74d65d832137e20e193c960802afba73b5d38sm * the hit to the parent game object. 25cfd74d65d832137e20e193c960802afba73b5d38sm */ 26cfd74d65d832137e20e193c960802afba73b5d38smpublic class BackgroundCollisionComponent extends GameComponent { 27cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mPreviousPosition; 28cfd74d65d832137e20e193c960802afba73b5d38sm private int mWidth; 29cfd74d65d832137e20e193c960802afba73b5d38sm private int mHeight; 30cfd74d65d832137e20e193c960802afba73b5d38sm private int mHorizontalOffset; 31cfd74d65d832137e20e193c960802afba73b5d38sm private int mVerticalOffset; 32cfd74d65d832137e20e193c960802afba73b5d38sm 33cfd74d65d832137e20e193c960802afba73b5d38sm // Workspace vectors. Allocated up front for speed. 34cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mCurrentPosition; 35cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mPreviousCenter; 36cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mDelta; 37cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mFilterDirection; 38cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mHorizontalHitPoint; 39cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mHorizontalHitNormal; 40cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mVerticalHitPoint; 41cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mVerticalHitNormal; 42cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mRayStart; 43cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mRayEnd; 44cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mTestPointStart; 45cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mTestPointEnd; 46cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mMergedNormal; 47cfd74d65d832137e20e193c960802afba73b5d38sm 48cfd74d65d832137e20e193c960802afba73b5d38sm /** 49cfd74d65d832137e20e193c960802afba73b5d38sm * Sets up the collision bounding box. This box may be a different size than the bounds of the 50cfd74d65d832137e20e193c960802afba73b5d38sm * sprite that this object controls. 51cfd74d65d832137e20e193c960802afba73b5d38sm * @param width The width of the collision box. 52cfd74d65d832137e20e193c960802afba73b5d38sm * @param height The height of the collision box. 53cfd74d65d832137e20e193c960802afba73b5d38sm * @param horzOffset The offset of the collision box from the object's origin in the x axis. 54cfd74d65d832137e20e193c960802afba73b5d38sm * @param vertOffset The offset of the collision box from the object's origin in the y axis. 55cfd74d65d832137e20e193c960802afba73b5d38sm */ 56cfd74d65d832137e20e193c960802afba73b5d38sm public BackgroundCollisionComponent(int width, int height, int horzOffset, int vertOffset) { 57cfd74d65d832137e20e193c960802afba73b5d38sm super(); 58cfd74d65d832137e20e193c960802afba73b5d38sm setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal()); 59cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousPosition = new Vector2(); 60cfd74d65d832137e20e193c960802afba73b5d38sm mWidth = width; 61cfd74d65d832137e20e193c960802afba73b5d38sm mHeight = height; 62cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalOffset = horzOffset; 63cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalOffset = vertOffset; 64cfd74d65d832137e20e193c960802afba73b5d38sm 65cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition = new Vector2(); 66cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousCenter = new Vector2(); 67cfd74d65d832137e20e193c960802afba73b5d38sm mDelta = new Vector2(); 68cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection = new Vector2(); 69cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitPoint = new Vector2(); 70cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal = new Vector2(); 71cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitPoint = new Vector2(); 72cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal = new Vector2(); 73cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart = new Vector2(); 74cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd = new Vector2(); 75cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart = new Vector2(); 76cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd = new Vector2(); 77cfd74d65d832137e20e193c960802afba73b5d38sm mMergedNormal = new Vector2(); 78cfd74d65d832137e20e193c960802afba73b5d38sm } 79cfd74d65d832137e20e193c960802afba73b5d38sm 80cfd74d65d832137e20e193c960802afba73b5d38sm public BackgroundCollisionComponent() { 81cfd74d65d832137e20e193c960802afba73b5d38sm super(); 82cfd74d65d832137e20e193c960802afba73b5d38sm setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal()); 83cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousPosition = new Vector2(); 84cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition = new Vector2(); 85cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousCenter = new Vector2(); 86cfd74d65d832137e20e193c960802afba73b5d38sm mDelta = new Vector2(); 87cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection = new Vector2(); 88cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitPoint = new Vector2(); 89cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal = new Vector2(); 90cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitPoint = new Vector2(); 91cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal = new Vector2(); 92cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart = new Vector2(); 93cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd = new Vector2(); 94cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart = new Vector2(); 95cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd = new Vector2(); 96cfd74d65d832137e20e193c960802afba73b5d38sm mMergedNormal = new Vector2(); 97cfd74d65d832137e20e193c960802afba73b5d38sm } 98cfd74d65d832137e20e193c960802afba73b5d38sm 99cfd74d65d832137e20e193c960802afba73b5d38sm @Override 100cfd74d65d832137e20e193c960802afba73b5d38sm public void reset() { 101cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousPosition.zero(); 102cfd74d65d832137e20e193c960802afba73b5d38sm } 103cfd74d65d832137e20e193c960802afba73b5d38sm 104cfd74d65d832137e20e193c960802afba73b5d38sm public void setSize(int width, int height) { 105cfd74d65d832137e20e193c960802afba73b5d38sm mWidth = width; 106cfd74d65d832137e20e193c960802afba73b5d38sm mHeight = height; 107cfd74d65d832137e20e193c960802afba73b5d38sm // TODO: Resize might cause new collisions. 108cfd74d65d832137e20e193c960802afba73b5d38sm } 109cfd74d65d832137e20e193c960802afba73b5d38sm 110cfd74d65d832137e20e193c960802afba73b5d38sm public void setOffset(int horzOffset, int vertOffset) { 111cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalOffset = horzOffset; 112cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalOffset = vertOffset; 113cfd74d65d832137e20e193c960802afba73b5d38sm } 114cfd74d65d832137e20e193c960802afba73b5d38sm 115cfd74d65d832137e20e193c960802afba73b5d38sm /** 116cfd74d65d832137e20e193c960802afba73b5d38sm * This function is the meat of the collision response logic. Our collision detection and 117cfd74d65d832137e20e193c960802afba73b5d38sm * response must be capable of dealing with arbitrary surfaces and must be frame rate 118cfd74d65d832137e20e193c960802afba73b5d38sm * independent (we must sweep the space in-between frames to find collisions reliably). The 119cfd74d65d832137e20e193c960802afba73b5d38sm * following algorithm is used to keep the collision box out of the collision world. 120cfd74d65d832137e20e193c960802afba73b5d38sm * 1. Cast a ray from the center point of the box at its position last frame to the edge 121cfd74d65d832137e20e193c960802afba73b5d38sm * of the box at its current position. If the ray intersects anything, snap the box 122cfd74d65d832137e20e193c960802afba73b5d38sm * back to the point of intersection. 123cfd74d65d832137e20e193c960802afba73b5d38sm * 2. Perform Step 1 twice: once looking for surfaces opposing horizontal movement and 124cfd74d65d832137e20e193c960802afba73b5d38sm * again for surfaces opposing vertical movement. These two ray tests approximate the 125cfd74d65d832137e20e193c960802afba73b5d38sm * movement of the box between the previous frame and this one. 126cfd74d65d832137e20e193c960802afba73b5d38sm * 3. Since most collisions are collisions with the ground, more precision is required for 127cfd74d65d832137e20e193c960802afba73b5d38sm * vertical intersections. Perform another ray test, this time from the top of the 128cfd74d65d832137e20e193c960802afba73b5d38sm * box's position (after snapping in Step 2) to the bottom. Snap out of any vertical 129cfd74d65d832137e20e193c960802afba73b5d38sm * surfaces that the ray encounters. This will ensure consistent snapping behavior on 130cfd74d65d832137e20e193c960802afba73b5d38sm * incline surfaces. 131cfd74d65d832137e20e193c960802afba73b5d38sm * 4. Add the normals of the surfaces that were hit up and normalize the result to produce 132cfd74d65d832137e20e193c960802afba73b5d38sm * a direction describing the average slope of the surfaces that the box is resting on. 133cfd74d65d832137e20e193c960802afba73b5d38sm * Physics will use this value as a normal to resolve collisions with the background. 134cfd74d65d832137e20e193c960802afba73b5d38sm */ 135cfd74d65d832137e20e193c960802afba73b5d38sm @Override 136cfd74d65d832137e20e193c960802afba73b5d38sm public void update(float timeDelta, BaseObject parent) { 137cfd74d65d832137e20e193c960802afba73b5d38sm GameObject parentObject = (GameObject) parent; 138cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setBackgroundCollisionNormal(Vector2.ZERO); 139cfd74d65d832137e20e193c960802afba73b5d38sm if (mPreviousPosition.length2() != 0) { 140cfd74d65d832137e20e193c960802afba73b5d38sm CollisionSystem collision = sSystemRegistry.collisionSystem; 141cfd74d65d832137e20e193c960802afba73b5d38sm if (collision != null) { 142cfd74d65d832137e20e193c960802afba73b5d38sm final int left = mHorizontalOffset; 143cfd74d65d832137e20e193c960802afba73b5d38sm final int bottom = mVerticalOffset; 144cfd74d65d832137e20e193c960802afba73b5d38sm final int right = left + mWidth; 145cfd74d65d832137e20e193c960802afba73b5d38sm final int top = bottom + mHeight; 146cfd74d65d832137e20e193c960802afba73b5d38sm final float centerOffsetX = ((mWidth) / 2.0f) + left; 147cfd74d65d832137e20e193c960802afba73b5d38sm final float centerOffsetY = ((mHeight) / 2.0f) + bottom; 148cfd74d65d832137e20e193c960802afba73b5d38sm 149cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.set(parentObject.getPosition()); 150cfd74d65d832137e20e193c960802afba73b5d38sm mDelta.set(mCurrentPosition); 151cfd74d65d832137e20e193c960802afba73b5d38sm mDelta.subtract(mPreviousPosition); 152cfd74d65d832137e20e193c960802afba73b5d38sm 153cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousCenter.set(centerOffsetX, centerOffsetY); 154cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousCenter.add(mPreviousPosition); 155cfd74d65d832137e20e193c960802afba73b5d38sm 156cfd74d65d832137e20e193c960802afba73b5d38sm boolean horizontalHit = false; 157cfd74d65d832137e20e193c960802afba73b5d38sm boolean verticalHit = false; 158cfd74d65d832137e20e193c960802afba73b5d38sm 159cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitPoint.zero(); 160cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal.zero(); 161cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitPoint.zero(); 162cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal.zero(); 163cfd74d65d832137e20e193c960802afba73b5d38sm 164cfd74d65d832137e20e193c960802afba73b5d38sm 165cfd74d65d832137e20e193c960802afba73b5d38sm // The order in which we sweep the horizontal and vertical space can affect the 166cfd74d65d832137e20e193c960802afba73b5d38sm // final result because we perform incremental snapping mid-sweep. So it is 167cfd74d65d832137e20e193c960802afba73b5d38sm // necessary to sweep in the primary direction of movement first. 168cfd74d65d832137e20e193c960802afba73b5d38sm if (Math.abs(mDelta.x) > Math.abs(mDelta.y)) { 169cfd74d65d832137e20e193c960802afba73b5d38sm horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left, 170cfd74d65d832137e20e193c960802afba73b5d38sm right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal, 171cfd74d65d832137e20e193c960802afba73b5d38sm parentObject); 172cfd74d65d832137e20e193c960802afba73b5d38sm verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom, 173cfd74d65d832137e20e193c960802afba73b5d38sm top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal, 174cfd74d65d832137e20e193c960802afba73b5d38sm parentObject); 175cfd74d65d832137e20e193c960802afba73b5d38sm 176cfd74d65d832137e20e193c960802afba73b5d38sm } else { 177cfd74d65d832137e20e193c960802afba73b5d38sm verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom, 178cfd74d65d832137e20e193c960802afba73b5d38sm top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal, 179cfd74d65d832137e20e193c960802afba73b5d38sm parentObject); 180cfd74d65d832137e20e193c960802afba73b5d38sm horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left, 181cfd74d65d832137e20e193c960802afba73b5d38sm right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal, 182cfd74d65d832137e20e193c960802afba73b5d38sm parentObject); 183cfd74d65d832137e20e193c960802afba73b5d38sm } 184cfd74d65d832137e20e193c960802afba73b5d38sm 185cfd74d65d832137e20e193c960802afba73b5d38sm // force the collision volume to stay within the bounds of the world. 186cfd74d65d832137e20e193c960802afba73b5d38sm LevelSystem level = sSystemRegistry.levelSystem; 187cfd74d65d832137e20e193c960802afba73b5d38sm if (level != null) { 188cfd74d65d832137e20e193c960802afba73b5d38sm if (mCurrentPosition.x + left < 0.0f) { 189cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.x = (-left + 1); 190cfd74d65d832137e20e193c960802afba73b5d38sm horizontalHit = true; 191cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal.x = (mHorizontalHitNormal.x + 1.0f); 192cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal.normalize(); 193cfd74d65d832137e20e193c960802afba73b5d38sm } else if (mCurrentPosition.x + right > level.getLevelWidth()) { 194cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.x = (level.getLevelWidth() - right - 1); 195cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal.x = (mHorizontalHitNormal.x - 1.0f); 196cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal.normalize(); 197cfd74d65d832137e20e193c960802afba73b5d38sm horizontalHit = true; 198cfd74d65d832137e20e193c960802afba73b5d38sm } 199cfd74d65d832137e20e193c960802afba73b5d38sm 200cfd74d65d832137e20e193c960802afba73b5d38sm /*if (mCurrentPosition.y + bottom < 0.0f) { 201cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.y = (-bottom + 1); 202cfd74d65d832137e20e193c960802afba73b5d38sm verticalHit = true; 203cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal.y = (mVerticalHitNormal.y + 1.0f); 204cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal.normalize(); 205cfd74d65d832137e20e193c960802afba73b5d38sm } else*/ if (mCurrentPosition.y + top > level.getLevelHeight()) { 206cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.y = (level.getLevelHeight() - top - 1); 207cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal.y = (mVerticalHitNormal.y - 1.0f); 208cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal.normalize(); 209cfd74d65d832137e20e193c960802afba73b5d38sm verticalHit = true; 210cfd74d65d832137e20e193c960802afba73b5d38sm } 211cfd74d65d832137e20e193c960802afba73b5d38sm 212cfd74d65d832137e20e193c960802afba73b5d38sm } 213cfd74d65d832137e20e193c960802afba73b5d38sm 214cfd74d65d832137e20e193c960802afba73b5d38sm 215cfd74d65d832137e20e193c960802afba73b5d38sm // One more set of tests to make sure that we are aligned with the surface. 216cfd74d65d832137e20e193c960802afba73b5d38sm // This time we will just check the inside of the bounding box for intersections. 217cfd74d65d832137e20e193c960802afba73b5d38sm // The sweep tests above will keep us out of collision in most cases, but this 218cfd74d65d832137e20e193c960802afba73b5d38sm // test will ensure that we are aligned to incline surfaces correctly. 219cfd74d65d832137e20e193c960802afba73b5d38sm 220cfd74d65d832137e20e193c960802afba73b5d38sm // Shoot a vertical line through the middle of the box. 221cfd74d65d832137e20e193c960802afba73b5d38sm if (mDelta.x != 0.0f && mDelta.y != 0.0f) { 222cfd74d65d832137e20e193c960802afba73b5d38sm float yStart = top; 223cfd74d65d832137e20e193c960802afba73b5d38sm float yEnd = bottom; 224cfd74d65d832137e20e193c960802afba73b5d38sm 225cfd74d65d832137e20e193c960802afba73b5d38sm 226cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart.set(centerOffsetX, yStart); 227cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart.add(mCurrentPosition); 228cfd74d65d832137e20e193c960802afba73b5d38sm 229cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd.set(centerOffsetX, yEnd); 230cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd.add(mCurrentPosition); 231cfd74d65d832137e20e193c960802afba73b5d38sm 232cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.set(mDelta); 233cfd74d65d832137e20e193c960802afba73b5d38sm 234cfd74d65d832137e20e193c960802afba73b5d38sm if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mVerticalHitPoint, 235cfd74d65d832137e20e193c960802afba73b5d38sm mVerticalHitNormal, parentObject)) { 236cfd74d65d832137e20e193c960802afba73b5d38sm 237cfd74d65d832137e20e193c960802afba73b5d38sm // If we found a collision, use this surface as our vertical intersection 238cfd74d65d832137e20e193c960802afba73b5d38sm // for this frame, even if the sweep above also found something. 239cfd74d65d832137e20e193c960802afba73b5d38sm verticalHit = true; 240cfd74d65d832137e20e193c960802afba73b5d38sm // snap 241cfd74d65d832137e20e193c960802afba73b5d38sm if (mVerticalHitNormal.y > 0.0f) { 242cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.y = (mVerticalHitPoint.y - bottom); 243cfd74d65d832137e20e193c960802afba73b5d38sm } else if (mVerticalHitNormal.y < 0.0f) { 244cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.y = (mVerticalHitPoint.y - top); 245cfd74d65d832137e20e193c960802afba73b5d38sm } 246cfd74d65d832137e20e193c960802afba73b5d38sm } 247cfd74d65d832137e20e193c960802afba73b5d38sm 248cfd74d65d832137e20e193c960802afba73b5d38sm 249cfd74d65d832137e20e193c960802afba73b5d38sm // Now the horizontal version of the same test 250cfd74d65d832137e20e193c960802afba73b5d38sm float xStart = left; 251cfd74d65d832137e20e193c960802afba73b5d38sm float xEnd = right; 252cfd74d65d832137e20e193c960802afba73b5d38sm if (mDelta.x < 0.0f) { 253cfd74d65d832137e20e193c960802afba73b5d38sm xStart = right; 254cfd74d65d832137e20e193c960802afba73b5d38sm xEnd = left; 255cfd74d65d832137e20e193c960802afba73b5d38sm } 256cfd74d65d832137e20e193c960802afba73b5d38sm 257cfd74d65d832137e20e193c960802afba73b5d38sm 258cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart.set(xStart, centerOffsetY); 259cfd74d65d832137e20e193c960802afba73b5d38sm mRayStart.add(mCurrentPosition); 260cfd74d65d832137e20e193c960802afba73b5d38sm 261cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd.set(xEnd, centerOffsetY); 262cfd74d65d832137e20e193c960802afba73b5d38sm mRayEnd.add(mCurrentPosition); 263cfd74d65d832137e20e193c960802afba73b5d38sm 264cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.set(mDelta); 265cfd74d65d832137e20e193c960802afba73b5d38sm 266cfd74d65d832137e20e193c960802afba73b5d38sm if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mHorizontalHitPoint, 267cfd74d65d832137e20e193c960802afba73b5d38sm mHorizontalHitNormal, parentObject)) { 268cfd74d65d832137e20e193c960802afba73b5d38sm 269cfd74d65d832137e20e193c960802afba73b5d38sm // If we found a collision, use this surface as our horizontal intersection 270cfd74d65d832137e20e193c960802afba73b5d38sm // for this frame, even if the sweep above also found something. 271cfd74d65d832137e20e193c960802afba73b5d38sm horizontalHit = true; 272cfd74d65d832137e20e193c960802afba73b5d38sm // snap 273cfd74d65d832137e20e193c960802afba73b5d38sm if (mHorizontalHitNormal.x > 0.0f) { 274cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.x = (mHorizontalHitPoint.x - left); 275cfd74d65d832137e20e193c960802afba73b5d38sm } else if (mHorizontalHitNormal.x < 0.0f) { 276cfd74d65d832137e20e193c960802afba73b5d38sm mCurrentPosition.x = (mHorizontalHitPoint.x - right); 277cfd74d65d832137e20e193c960802afba73b5d38sm } 278cfd74d65d832137e20e193c960802afba73b5d38sm } 279cfd74d65d832137e20e193c960802afba73b5d38sm } 280cfd74d65d832137e20e193c960802afba73b5d38sm 281cfd74d65d832137e20e193c960802afba73b5d38sm 282cfd74d65d832137e20e193c960802afba73b5d38sm // Record the intersection for other systems to use. 283cfd74d65d832137e20e193c960802afba73b5d38sm final TimeSystem timeSystem = sSystemRegistry.timeSystem; 284cfd74d65d832137e20e193c960802afba73b5d38sm 285cfd74d65d832137e20e193c960802afba73b5d38sm if (timeSystem != null) { 286cfd74d65d832137e20e193c960802afba73b5d38sm float time = timeSystem.getGameTime(); 287cfd74d65d832137e20e193c960802afba73b5d38sm if (horizontalHit) { 288cfd74d65d832137e20e193c960802afba73b5d38sm if (mHorizontalHitNormal.x > 0.0f) { 289cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setLastTouchedLeftWallTime(time); 290cfd74d65d832137e20e193c960802afba73b5d38sm } else { 291cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setLastTouchedRightWallTime(time); 292cfd74d65d832137e20e193c960802afba73b5d38sm } 293cfd74d65d832137e20e193c960802afba73b5d38sm //parentObject.setBackgroundCollisionNormal(mHorizontalHitNormal); 294cfd74d65d832137e20e193c960802afba73b5d38sm } 295cfd74d65d832137e20e193c960802afba73b5d38sm 296cfd74d65d832137e20e193c960802afba73b5d38sm if (verticalHit) { 297cfd74d65d832137e20e193c960802afba73b5d38sm if (mVerticalHitNormal.y > 0.0f) { 298cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setLastTouchedFloorTime(time); 299cfd74d65d832137e20e193c960802afba73b5d38sm } else { 300cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setLastTouchedCeilingTime(time); 301cfd74d65d832137e20e193c960802afba73b5d38sm } 302cfd74d65d832137e20e193c960802afba73b5d38sm //parentObject.setBackgroundCollisionNormal(mVerticalHitNormal); 303cfd74d65d832137e20e193c960802afba73b5d38sm } 304cfd74d65d832137e20e193c960802afba73b5d38sm 305cfd74d65d832137e20e193c960802afba73b5d38sm 306cfd74d65d832137e20e193c960802afba73b5d38sm // If we hit multiple surfaces, merge their normals together to produce an 307cfd74d65d832137e20e193c960802afba73b5d38sm // average direction of obstruction. 308cfd74d65d832137e20e193c960802afba73b5d38sm if (true) { //(verticalHit && horizontalHit) { 309cfd74d65d832137e20e193c960802afba73b5d38sm mMergedNormal.set(mVerticalHitNormal); 310cfd74d65d832137e20e193c960802afba73b5d38sm mMergedNormal.add(mHorizontalHitNormal); 311cfd74d65d832137e20e193c960802afba73b5d38sm mMergedNormal.normalize(); 312cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setBackgroundCollisionNormal(mMergedNormal); 313cfd74d65d832137e20e193c960802afba73b5d38sm } 314cfd74d65d832137e20e193c960802afba73b5d38sm 315cfd74d65d832137e20e193c960802afba73b5d38sm parentObject.setPosition(mCurrentPosition); 316cfd74d65d832137e20e193c960802afba73b5d38sm } 317cfd74d65d832137e20e193c960802afba73b5d38sm 318cfd74d65d832137e20e193c960802afba73b5d38sm } 319cfd74d65d832137e20e193c960802afba73b5d38sm } 320cfd74d65d832137e20e193c960802afba73b5d38sm mPreviousPosition.set(parentObject.getPosition()); 321cfd74d65d832137e20e193c960802afba73b5d38sm } 322cfd74d65d832137e20e193c960802afba73b5d38sm 323cfd74d65d832137e20e193c960802afba73b5d38sm /* Sweeps the space between two points looking for surfaces that oppose horizontal movement. */ 324cfd74d65d832137e20e193c960802afba73b5d38sm protected boolean sweepHorizontal(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta, 325cfd74d65d832137e20e193c960802afba73b5d38sm int left, int right, float centerY, Vector2 hitPoint, Vector2 hitNormal, 326cfd74d65d832137e20e193c960802afba73b5d38sm GameObject parentObject) { 327cfd74d65d832137e20e193c960802afba73b5d38sm boolean hit = false; 328cfd74d65d832137e20e193c960802afba73b5d38sm if (!Utils.close(delta.x, 0.0f)) { 329cfd74d65d832137e20e193c960802afba73b5d38sm CollisionSystem collision = sSystemRegistry.collisionSystem; 330cfd74d65d832137e20e193c960802afba73b5d38sm 331cfd74d65d832137e20e193c960802afba73b5d38sm // Shoot a ray from the center of the previous frame's box to the edge (left or right, 332cfd74d65d832137e20e193c960802afba73b5d38sm // depending on the direction of movement) of the current box. 333cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.y = (centerY); 334cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.x = (left); 335cfd74d65d832137e20e193c960802afba73b5d38sm int offset = -left; 336cfd74d65d832137e20e193c960802afba73b5d38sm if (delta.x > 0.0f) { 337cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.x = (right); 338cfd74d65d832137e20e193c960802afba73b5d38sm offset = -right; 339cfd74d65d832137e20e193c960802afba73b5d38sm } 340cfd74d65d832137e20e193c960802afba73b5d38sm 341cfd74d65d832137e20e193c960802afba73b5d38sm // Filter out surfaces that do not oppose motion in the horizontal direction, or 342cfd74d65d832137e20e193c960802afba73b5d38sm // push in the same direction as movement. 343cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.set(delta); 344cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.y = (0); 345cfd74d65d832137e20e193c960802afba73b5d38sm 346cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd.set(currentPosition); 347cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd.add(mTestPointStart); 348cfd74d65d832137e20e193c960802afba73b5d38sm if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection, 349cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, parentObject)) { 350cfd74d65d832137e20e193c960802afba73b5d38sm // snap 351cfd74d65d832137e20e193c960802afba73b5d38sm currentPosition.x = (hitPoint.x + offset); 352cfd74d65d832137e20e193c960802afba73b5d38sm hit = true; 353cfd74d65d832137e20e193c960802afba73b5d38sm } 354cfd74d65d832137e20e193c960802afba73b5d38sm } 355cfd74d65d832137e20e193c960802afba73b5d38sm return hit; 356cfd74d65d832137e20e193c960802afba73b5d38sm } 357cfd74d65d832137e20e193c960802afba73b5d38sm 358cfd74d65d832137e20e193c960802afba73b5d38sm /* Sweeps the space between two points looking for surfaces that oppose vertical movement. */ 359cfd74d65d832137e20e193c960802afba73b5d38sm protected boolean sweepVertical(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta, 360cfd74d65d832137e20e193c960802afba73b5d38sm int bottom, int top, float centerX, Vector2 hitPoint, Vector2 hitNormal, 361cfd74d65d832137e20e193c960802afba73b5d38sm GameObject parentObject) { 362cfd74d65d832137e20e193c960802afba73b5d38sm boolean hit = false; 363cfd74d65d832137e20e193c960802afba73b5d38sm if (!Utils.close(delta.y, 0.0f)) { 364cfd74d65d832137e20e193c960802afba73b5d38sm CollisionSystem collision = sSystemRegistry.collisionSystem; 365cfd74d65d832137e20e193c960802afba73b5d38sm // Shoot a ray from the center of the previous frame's box to the edge (top or bottom, 366cfd74d65d832137e20e193c960802afba73b5d38sm // depending on the direction of movement) of the current box. 367cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.x = (centerX); 368cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.y = (bottom); 369cfd74d65d832137e20e193c960802afba73b5d38sm int offset = -bottom; 370cfd74d65d832137e20e193c960802afba73b5d38sm if (delta.y > 0.0f) { 371cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointStart.y = (top); 372cfd74d65d832137e20e193c960802afba73b5d38sm offset = -top; 373cfd74d65d832137e20e193c960802afba73b5d38sm } 374cfd74d65d832137e20e193c960802afba73b5d38sm 375cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.set(delta); 376cfd74d65d832137e20e193c960802afba73b5d38sm mFilterDirection.x = (0); 377cfd74d65d832137e20e193c960802afba73b5d38sm 378cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd.set(currentPosition); 379cfd74d65d832137e20e193c960802afba73b5d38sm mTestPointEnd.add(mTestPointStart); 380cfd74d65d832137e20e193c960802afba73b5d38sm if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection, 381cfd74d65d832137e20e193c960802afba73b5d38sm hitPoint, hitNormal, parentObject)) { 382cfd74d65d832137e20e193c960802afba73b5d38sm hit = true; 383cfd74d65d832137e20e193c960802afba73b5d38sm // snap 384cfd74d65d832137e20e193c960802afba73b5d38sm currentPosition.y = (hitPoint.y + offset); 385cfd74d65d832137e20e193c960802afba73b5d38sm } 386cfd74d65d832137e20e193c960802afba73b5d38sm 387cfd74d65d832137e20e193c960802afba73b5d38sm } 388cfd74d65d832137e20e193c960802afba73b5d38sm return hit; 389cfd74d65d832137e20e193c960802afba73b5d38sm } 390cfd74d65d832137e20e193c960802afba73b5d38sm 391cfd74d65d832137e20e193c960802afba73b5d38sm /** Comparator for hit points. */ 392cfd74d65d832137e20e193c960802afba73b5d38sm private static class HitPointDistanceComparator implements Comparator<HitPoint> { 393cfd74d65d832137e20e193c960802afba73b5d38sm private Vector2 mOrigin; 394cfd74d65d832137e20e193c960802afba73b5d38sm 395cfd74d65d832137e20e193c960802afba73b5d38sm public HitPointDistanceComparator() { 396cfd74d65d832137e20e193c960802afba73b5d38sm super(); 397cfd74d65d832137e20e193c960802afba73b5d38sm mOrigin = new Vector2(); 398cfd74d65d832137e20e193c960802afba73b5d38sm } 399cfd74d65d832137e20e193c960802afba73b5d38sm 400cfd74d65d832137e20e193c960802afba73b5d38sm public final void setOrigin(Vector2 origin) { 401cfd74d65d832137e20e193c960802afba73b5d38sm mOrigin.set(origin); 402cfd74d65d832137e20e193c960802afba73b5d38sm } 403cfd74d65d832137e20e193c960802afba73b5d38sm 404cfd74d65d832137e20e193c960802afba73b5d38sm public final void setOrigin(float x, float y) { 405cfd74d65d832137e20e193c960802afba73b5d38sm mOrigin.set(x, y); 406cfd74d65d832137e20e193c960802afba73b5d38sm } 407cfd74d65d832137e20e193c960802afba73b5d38sm 408cfd74d65d832137e20e193c960802afba73b5d38sm public int compare(HitPoint object1, HitPoint object2) { 409cfd74d65d832137e20e193c960802afba73b5d38sm int result = 0; 410cfd74d65d832137e20e193c960802afba73b5d38sm if (object1 != null && object2 != null) { 411cfd74d65d832137e20e193c960802afba73b5d38sm final float obj1Distance = object1.hitPoint.distance2(mOrigin); 412cfd74d65d832137e20e193c960802afba73b5d38sm final float obj2Distance = object2.hitPoint.distance2(mOrigin); 413cfd74d65d832137e20e193c960802afba73b5d38sm final float distanceDelta = obj1Distance - obj2Distance; 414cfd74d65d832137e20e193c960802afba73b5d38sm result = distanceDelta < 0.0f ? -1 : 1; 415cfd74d65d832137e20e193c960802afba73b5d38sm } else if (object1 == null && object2 != null) { 416cfd74d65d832137e20e193c960802afba73b5d38sm result = 1; 417cfd74d65d832137e20e193c960802afba73b5d38sm } else if (object2 == null && object1 != null) { 418cfd74d65d832137e20e193c960802afba73b5d38sm result = -1; 419cfd74d65d832137e20e193c960802afba73b5d38sm } 420cfd74d65d832137e20e193c960802afba73b5d38sm return result; 421cfd74d65d832137e20e193c960802afba73b5d38sm } 422cfd74d65d832137e20e193c960802afba73b5d38sm } 423cfd74d65d832137e20e193c960802afba73b5d38sm} 424