Home | History | Annotate | Download | only in replicaisland
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.replica.replicaisland;
     18 
     19 /**
     20  * Manages the position of the camera based on a target game object.
     21  */
     22 public class CameraSystem extends BaseObject {
     23     private GameObject mTarget;
     24     private float mShakeTime;
     25     private float mShakeMagnitude;
     26     private float mShakeOffsetY;
     27     private Vector2 mCurrentCameraPosition;
     28     private Vector2 mFocalPosition;
     29     private Vector2 mPreInterpolateCameraPosition;
     30     private Vector2 mTargetPosition;
     31     private Vector2 mBias;
     32     private float mTargetChangedTime;
     33 
     34     private static final float X_FOLLOW_DISTANCE = 0.0f;
     35     private static final float Y_UP_FOLLOW_DISTANCE = 90.0f;
     36     private static final float Y_DOWN_FOLLOW_DISTANCE = 0.0f;
     37 
     38     private static final float MAX_INTERPOLATE_TO_TARGET_DISTANCE = 300.0f;
     39     private static final float INTERPOLATE_TO_TARGET_TIME = 1.0f;
     40 
     41     private static int SHAKE_FREQUENCY = 40;
     42 
     43     private static float BIAS_SPEED = 400.0f;
     44 
     45     public CameraSystem() {
     46         super();
     47         mCurrentCameraPosition = new Vector2();
     48         mFocalPosition = new Vector2();
     49         mPreInterpolateCameraPosition = new Vector2();
     50         mTargetPosition = new Vector2();
     51         mBias = new Vector2();
     52     }
     53 
     54     @Override
     55     public void reset() {
     56         mTarget = null;
     57         mCurrentCameraPosition.zero();
     58         mShakeTime = 0.0f;
     59         mShakeMagnitude = 0.0f;
     60         mFocalPosition.zero();
     61         mTargetChangedTime = 0.0f;
     62         mPreInterpolateCameraPosition.zero();
     63         mTargetPosition.zero();
     64     }
     65 
     66     void setTarget(GameObject target) {
     67         if (target != null && mTarget != target) {
     68             mPreInterpolateCameraPosition.set(mCurrentCameraPosition);
     69             mPreInterpolateCameraPosition.subtract(target.getPosition());
     70             if (mPreInterpolateCameraPosition.length2() <
     71                     MAX_INTERPOLATE_TO_TARGET_DISTANCE * MAX_INTERPOLATE_TO_TARGET_DISTANCE) {
     72                 final TimeSystem time = sSystemRegistry.timeSystem;
     73                 mTargetChangedTime = time.getGameTime();
     74                 mPreInterpolateCameraPosition.set(mCurrentCameraPosition);
     75             } else {
     76             	mTargetChangedTime = 0.0f;
     77                 mCurrentCameraPosition.set(target.getPosition());
     78             }
     79         }
     80 
     81         mTarget = target;
     82 
     83     }
     84 
     85     public GameObject getTarget() {
     86 		return mTarget;
     87 	}
     88 
     89     void shake(float duration, float magnitude) {
     90         mShakeTime = duration;
     91         mShakeMagnitude = magnitude;
     92     }
     93 
     94     public boolean shaking() {
     95         return mShakeTime > 0.0f;
     96     }
     97 
     98     @Override
     99     public void update(float timeDelta, BaseObject parent) {
    100 
    101         mShakeOffsetY = 0.0f;
    102 
    103         if (mShakeTime > 0.0f) {
    104             mShakeTime -= timeDelta;
    105             mShakeOffsetY = (float) (Math.sin(mShakeTime * SHAKE_FREQUENCY) * mShakeMagnitude);
    106         }
    107 
    108         if (mTarget != null) {
    109         	mTargetPosition.set(mTarget.getCenteredPositionX(), mTarget.getCenteredPositionY());
    110             final Vector2 targetPosition = mTargetPosition;
    111 
    112             if (mTargetChangedTime > 0.0f) {
    113                 final TimeSystem time = sSystemRegistry.timeSystem;
    114                 final float delta = time.getGameTime() - mTargetChangedTime;
    115 
    116                 mCurrentCameraPosition.x = Lerp.ease(mPreInterpolateCameraPosition.x,
    117                         targetPosition.x, INTERPOLATE_TO_TARGET_TIME, delta);
    118 
    119                 mCurrentCameraPosition.y = Lerp.ease(mPreInterpolateCameraPosition.y,
    120                         targetPosition.y, INTERPOLATE_TO_TARGET_TIME, delta);
    121 
    122                 if (delta > INTERPOLATE_TO_TARGET_TIME) {
    123                     mTargetChangedTime = -1;
    124                 }
    125             } else {
    126 
    127             	// Only respect the bias if the target is moving.  No camera motion without
    128             	// player input!
    129             	if (mBias.length2() > 0.0f && mTarget.getVelocity().length2() > 1.0f) {
    130                 	mBias.normalize();
    131                 	mBias.multiply(BIAS_SPEED * timeDelta);
    132                 	mCurrentCameraPosition.add(mBias);
    133                 }
    134 
    135                 final float xDelta = targetPosition.x - mCurrentCameraPosition.x;
    136                 if (Math.abs(xDelta) > X_FOLLOW_DISTANCE) {
    137                     mCurrentCameraPosition.x = targetPosition.x - (X_FOLLOW_DISTANCE * Utils.sign(xDelta));
    138                 }
    139 
    140 
    141                 final float yDelta = targetPosition.y - mCurrentCameraPosition.y;
    142                 if (yDelta > Y_UP_FOLLOW_DISTANCE) {
    143                     mCurrentCameraPosition.y = targetPosition.y - Y_UP_FOLLOW_DISTANCE;
    144                 } else if (yDelta < -Y_DOWN_FOLLOW_DISTANCE) {
    145                     mCurrentCameraPosition.y = targetPosition.y + Y_DOWN_FOLLOW_DISTANCE;
    146                 }
    147 
    148             }
    149 
    150         	mBias.zero();
    151 
    152         }
    153 
    154         mFocalPosition.x = (float) Math.floor(mCurrentCameraPosition.x);
    155         mFocalPosition.x = snapFocalPointToWorldBoundsX(mFocalPosition.x);
    156 
    157         mFocalPosition.y = (float) Math.floor(mCurrentCameraPosition.y + mShakeOffsetY);
    158         mFocalPosition.y = snapFocalPointToWorldBoundsY(mFocalPosition.y);
    159     }
    160 
    161     /** Returns the x position of the camera's look-at point. */
    162     public float getFocusPositionX() {
    163         return mFocalPosition.x;
    164     }
    165 
    166     /** Returns the y position of the camera's look-at point. */
    167     public float getFocusPositionY() {
    168         return mFocalPosition.y;
    169     }
    170 
    171     public boolean pointVisible(Vector2 point, float radius) {
    172         boolean visible = false;
    173         final float width = sSystemRegistry.contextParameters.gameWidth / 2.0f;
    174         final float height = sSystemRegistry.contextParameters.gameHeight / 2.0f;
    175         if (Math.abs(mFocalPosition.x - point.x) < (width + radius)) {
    176             if (Math.abs(mFocalPosition.y - point.y) < (height + radius)) {
    177                 visible = true;
    178             }
    179         }
    180         return visible;
    181     }
    182 
    183     /** Snaps a coordinate against the bounds of the world so that it may not pass out
    184      * of the visible area of the world.
    185      * @param worldX An x-coordinate in world units.
    186      * @return An x-coordinate that is guaranteed not to expose the edges of the world.
    187      */
    188     public float snapFocalPointToWorldBoundsX(float worldX) {
    189         float focalPositionX = worldX;
    190         final float width = sSystemRegistry.contextParameters.gameWidth;
    191         final LevelSystem level = sSystemRegistry.levelSystem;
    192         if (level != null) {
    193             final float worldPixelWidth = Math.max(level.getLevelWidth(), width);
    194             final float rightEdge = focalPositionX + (width / 2.0f);
    195             final float leftEdge = focalPositionX - (width / 2.0f);
    196 
    197             if (rightEdge > worldPixelWidth) {
    198                 focalPositionX = worldPixelWidth - (width / 2.0f);
    199             } else if (leftEdge < 0) {
    200                 focalPositionX = width / 2.0f;
    201             }
    202         }
    203         return focalPositionX;
    204     }
    205 
    206     /** Snaps a coordinate against the bounds of the world so that it may not pass out
    207      * of the visible area of the world.
    208      * @param worldY A y-coordinate in world units.
    209      * @return A y-coordinate that is guaranteed not to expose the edges of the world.
    210      */
    211     public float snapFocalPointToWorldBoundsY(float worldY) {
    212         float focalPositionY = worldY;
    213 
    214         final float height = sSystemRegistry.contextParameters.gameHeight;
    215         final LevelSystem level = sSystemRegistry.levelSystem;
    216         if (level != null) {
    217             final float worldPixelHeight = Math.max(level.getLevelHeight(), sSystemRegistry.contextParameters.gameHeight);
    218             final float topEdge = focalPositionY + (height / 2.0f);
    219             final float bottomEdge = focalPositionY - (height / 2.0f);
    220 
    221             if (topEdge > worldPixelHeight) {
    222                 focalPositionY = worldPixelHeight - (height / 2.0f);
    223             } else if (bottomEdge < 0) {
    224                 focalPositionY = height / 2.0f;
    225             }
    226         }
    227 
    228         return focalPositionY;
    229     }
    230 
    231 	public void addCameraBias(Vector2 bias) {
    232 		final float x = bias.x - mFocalPosition.x;
    233 		final float y = bias.y - mFocalPosition.y;
    234 		final float biasX = mBias.x;
    235 		final float biasY = mBias.y;
    236 		mBias.set(x, y);
    237 		mBias.normalize();
    238 		mBias.add(biasX, biasY);
    239 	}
    240 
    241 
    242 }
    243