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
17cfd74d65d832137e20e193c960802afba73b5d38smpackage com.replica.replicaisland;
18cfd74d65d832137e20e193c960802afba73b5d38sm
19cfd74d65d832137e20e193c960802afba73b5d38smimport java.util.Comparator;
20cfd74d65d832137e20e193c960802afba73b5d38sm
21cfd74d65d832137e20e193c960802afba73b5d38sm/**
22cfd74d65d832137e20e193c960802afba73b5d38sm * A node in the game graph that manages the activation status of its children.  The
23cfd74d65d832137e20e193c960802afba73b5d38sm * GameObjectManager moves the objects it manages in and out of the active list (that is,
24cfd74d65d832137e20e193c960802afba73b5d38sm * in and out of the game tree, causing them to be updated or ignored, respectively) each frame
25cfd74d65d832137e20e193c960802afba73b5d38sm * based on the distance of that object to the camera.  Objects may specify an "activation radius"
26cfd74d65d832137e20e193c960802afba73b5d38sm * to define an area around themselves so that the position of the camera can be used to determine
27cfd74d65d832137e20e193c960802afba73b5d38sm * which objects should receive processing time and which should be ignored.  Objects that do not
28cfd74d65d832137e20e193c960802afba73b5d38sm * move should have an activation radius that defines a sphere similar to the size of the screen;
29cfd74d65d832137e20e193c960802afba73b5d38sm * they only need processing when they are visible.  Objects that move around will probably need
30cfd74d65d832137e20e193c960802afba73b5d38sm * larger regions so that they can leave the visible area of the game world and not be immediately
31cfd74d65d832137e20e193c960802afba73b5d38sm * deactivated.
32cfd74d65d832137e20e193c960802afba73b5d38sm */
33cfd74d65d832137e20e193c960802afba73b5d38smpublic class GameObjectManager extends ObjectManager {
349d4cc2572d37983607df38b0f4216ed76ac51814sm    private static final int MAX_GAME_OBJECTS = 384;
35cfd74d65d832137e20e193c960802afba73b5d38sm    private float mMaxActivationRadius;
36cfd74d65d832137e20e193c960802afba73b5d38sm    private final static HorizontalPositionComparator sGameObjectComparator
37cfd74d65d832137e20e193c960802afba73b5d38sm        = new HorizontalPositionComparator();
38cfd74d65d832137e20e193c960802afba73b5d38sm    private FixedSizeArray<BaseObject> mInactiveObjects;
39cfd74d65d832137e20e193c960802afba73b5d38sm    private FixedSizeArray<GameObject> mMarkedForDeathObjects;
40cfd74d65d832137e20e193c960802afba73b5d38sm    private GameObject mPlayer;
41cfd74d65d832137e20e193c960802afba73b5d38sm    private boolean mVisitingGraph;
42cfd74d65d832137e20e193c960802afba73b5d38sm    private Vector2 mCameraFocus;
43cfd74d65d832137e20e193c960802afba73b5d38sm
44cfd74d65d832137e20e193c960802afba73b5d38sm
45cfd74d65d832137e20e193c960802afba73b5d38sm    public GameObjectManager(float maxActivationRadius) {
46cfd74d65d832137e20e193c960802afba73b5d38sm        super(MAX_GAME_OBJECTS);
47cfd74d65d832137e20e193c960802afba73b5d38sm        mMaxActivationRadius = maxActivationRadius;
48cfd74d65d832137e20e193c960802afba73b5d38sm
49cfd74d65d832137e20e193c960802afba73b5d38sm        mInactiveObjects = new FixedSizeArray<BaseObject>(MAX_GAME_OBJECTS);
50cfd74d65d832137e20e193c960802afba73b5d38sm        mInactiveObjects.setComparator(sGameObjectComparator);
51cfd74d65d832137e20e193c960802afba73b5d38sm
52cfd74d65d832137e20e193c960802afba73b5d38sm        mMarkedForDeathObjects = new FixedSizeArray<GameObject>(MAX_GAME_OBJECTS);
53cfd74d65d832137e20e193c960802afba73b5d38sm        mVisitingGraph = false;
54cfd74d65d832137e20e193c960802afba73b5d38sm
55cfd74d65d832137e20e193c960802afba73b5d38sm        mCameraFocus = new Vector2();
56cfd74d65d832137e20e193c960802afba73b5d38sm
57cfd74d65d832137e20e193c960802afba73b5d38sm    }
58cfd74d65d832137e20e193c960802afba73b5d38sm
59cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
60cfd74d65d832137e20e193c960802afba73b5d38sm    public void commitUpdates() {
61cfd74d65d832137e20e193c960802afba73b5d38sm        super.commitUpdates();
62cfd74d65d832137e20e193c960802afba73b5d38sm
63cfd74d65d832137e20e193c960802afba73b5d38sm        GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
64cfd74d65d832137e20e193c960802afba73b5d38sm        final int objectsToKillCount = mMarkedForDeathObjects.getCount();
65cfd74d65d832137e20e193c960802afba73b5d38sm        if (factory != null && objectsToKillCount > 0) {
66cfd74d65d832137e20e193c960802afba73b5d38sm            final Object[] deathArray = mMarkedForDeathObjects.getArray();
67cfd74d65d832137e20e193c960802afba73b5d38sm            for (int x = 0; x < objectsToKillCount; x++) {
68cfd74d65d832137e20e193c960802afba73b5d38sm                factory.destroy((GameObject)deathArray[x]);
69cfd74d65d832137e20e193c960802afba73b5d38sm            }
70cfd74d65d832137e20e193c960802afba73b5d38sm            mMarkedForDeathObjects.clear();
71cfd74d65d832137e20e193c960802afba73b5d38sm        }
72cfd74d65d832137e20e193c960802afba73b5d38sm    }
73cfd74d65d832137e20e193c960802afba73b5d38sm
74cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
75cfd74d65d832137e20e193c960802afba73b5d38sm    public void update(float timeDelta, BaseObject parent) {
76cfd74d65d832137e20e193c960802afba73b5d38sm        commitUpdates();
77cfd74d65d832137e20e193c960802afba73b5d38sm
78cfd74d65d832137e20e193c960802afba73b5d38sm        CameraSystem camera = sSystemRegistry.cameraSystem;
79cfd74d65d832137e20e193c960802afba73b5d38sm
80cfd74d65d832137e20e193c960802afba73b5d38sm        mCameraFocus.set(camera.getFocusPositionX(), camera.getFocusPositionY());
81cfd74d65d832137e20e193c960802afba73b5d38sm        mVisitingGraph = true;
82cfd74d65d832137e20e193c960802afba73b5d38sm        FixedSizeArray<BaseObject> objects = getObjects();
83cfd74d65d832137e20e193c960802afba73b5d38sm        final int count = objects.getCount();
84cfd74d65d832137e20e193c960802afba73b5d38sm
85cfd74d65d832137e20e193c960802afba73b5d38sm        if (count > 0) {
86cfd74d65d832137e20e193c960802afba73b5d38sm            final Object[] objectArray = objects.getArray();
87cfd74d65d832137e20e193c960802afba73b5d38sm            for (int i = count - 1; i >= 0; i--) {
88cfd74d65d832137e20e193c960802afba73b5d38sm                GameObject gameObject = (GameObject)objectArray[i];
89cfd74d65d832137e20e193c960802afba73b5d38sm                final float distance2 = mCameraFocus.distance2(gameObject.getPosition());
90cfd74d65d832137e20e193c960802afba73b5d38sm                if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
91cfd74d65d832137e20e193c960802afba73b5d38sm                        || gameObject.activationRadius == -1) {
92cfd74d65d832137e20e193c960802afba73b5d38sm                    gameObject.update(timeDelta, this);
93cfd74d65d832137e20e193c960802afba73b5d38sm                } else {
94cfd74d65d832137e20e193c960802afba73b5d38sm                	// Remove the object from the list.
95cfd74d65d832137e20e193c960802afba73b5d38sm                	// It's safe to just swap the current object with the last
96cfd74d65d832137e20e193c960802afba73b5d38sm                	// object because this list is being iterated backwards, so
97cfd74d65d832137e20e193c960802afba73b5d38sm                	// the last object in the list has already been processed.
98cfd74d65d832137e20e193c960802afba73b5d38sm                	objects.swapWithLast(i);
99cfd74d65d832137e20e193c960802afba73b5d38sm                    objects.removeLast();
100cfd74d65d832137e20e193c960802afba73b5d38sm                    if (gameObject.destroyOnDeactivation) {
101cfd74d65d832137e20e193c960802afba73b5d38sm                        mMarkedForDeathObjects.add(gameObject);
102cfd74d65d832137e20e193c960802afba73b5d38sm                    } else {
103cfd74d65d832137e20e193c960802afba73b5d38sm                        mInactiveObjects.add((BaseObject)gameObject);
104cfd74d65d832137e20e193c960802afba73b5d38sm                    }
105cfd74d65d832137e20e193c960802afba73b5d38sm                }
106cfd74d65d832137e20e193c960802afba73b5d38sm            }
107cfd74d65d832137e20e193c960802afba73b5d38sm        }
108cfd74d65d832137e20e193c960802afba73b5d38sm
109cfd74d65d832137e20e193c960802afba73b5d38sm        mInactiveObjects.sort(false);
110cfd74d65d832137e20e193c960802afba73b5d38sm        final int inactiveCount = mInactiveObjects.getCount();
111cfd74d65d832137e20e193c960802afba73b5d38sm        if (inactiveCount > 0) {
112cfd74d65d832137e20e193c960802afba73b5d38sm            final Object[] inactiveArray = mInactiveObjects.getArray();
113cfd74d65d832137e20e193c960802afba73b5d38sm            for (int i = inactiveCount - 1; i >= 0; i--) {
114cfd74d65d832137e20e193c960802afba73b5d38sm                GameObject gameObject = (GameObject)inactiveArray[i];
115cfd74d65d832137e20e193c960802afba73b5d38sm
116cfd74d65d832137e20e193c960802afba73b5d38sm                final Vector2 position = gameObject.getPosition();
117cfd74d65d832137e20e193c960802afba73b5d38sm                final float distance2 = mCameraFocus.distance2(position);
118cfd74d65d832137e20e193c960802afba73b5d38sm                final float xDistance = position.x - mCameraFocus.x;
119cfd74d65d832137e20e193c960802afba73b5d38sm                if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
120cfd74d65d832137e20e193c960802afba73b5d38sm                        || gameObject.activationRadius == -1) {
121cfd74d65d832137e20e193c960802afba73b5d38sm                    gameObject.update(timeDelta, this);
122cfd74d65d832137e20e193c960802afba73b5d38sm                    mInactiveObjects.swapWithLast(i);
123cfd74d65d832137e20e193c960802afba73b5d38sm                    mInactiveObjects.removeLast();
124cfd74d65d832137e20e193c960802afba73b5d38sm                    objects.add(gameObject);
125cfd74d65d832137e20e193c960802afba73b5d38sm                } else if (xDistance < -mMaxActivationRadius) {
126cfd74d65d832137e20e193c960802afba73b5d38sm                    // We've passed the focus, we can stop processing now
127cfd74d65d832137e20e193c960802afba73b5d38sm                    break;
128cfd74d65d832137e20e193c960802afba73b5d38sm                }
129cfd74d65d832137e20e193c960802afba73b5d38sm            }
130cfd74d65d832137e20e193c960802afba73b5d38sm        }
131cfd74d65d832137e20e193c960802afba73b5d38sm        mVisitingGraph = false;
132cfd74d65d832137e20e193c960802afba73b5d38sm    }
133cfd74d65d832137e20e193c960802afba73b5d38sm
134cfd74d65d832137e20e193c960802afba73b5d38sm
135cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
136cfd74d65d832137e20e193c960802afba73b5d38sm    public void add(BaseObject object) {
137cfd74d65d832137e20e193c960802afba73b5d38sm        if (object instanceof GameObject) {
138cfd74d65d832137e20e193c960802afba73b5d38sm            super.add(object);
139cfd74d65d832137e20e193c960802afba73b5d38sm        }
140cfd74d65d832137e20e193c960802afba73b5d38sm    }
141cfd74d65d832137e20e193c960802afba73b5d38sm
142cfd74d65d832137e20e193c960802afba73b5d38sm    @Override
143cfd74d65d832137e20e193c960802afba73b5d38sm    public void remove(BaseObject object) {
144cfd74d65d832137e20e193c960802afba73b5d38sm        super.remove(object);
145cfd74d65d832137e20e193c960802afba73b5d38sm        if (object == mPlayer) {
146cfd74d65d832137e20e193c960802afba73b5d38sm            mPlayer = null;
147cfd74d65d832137e20e193c960802afba73b5d38sm        }
148cfd74d65d832137e20e193c960802afba73b5d38sm    }
149cfd74d65d832137e20e193c960802afba73b5d38sm
150cfd74d65d832137e20e193c960802afba73b5d38sm    public void destroy(GameObject object) {
151cfd74d65d832137e20e193c960802afba73b5d38sm        mMarkedForDeathObjects.add(object);
152cfd74d65d832137e20e193c960802afba73b5d38sm        remove(object);
153cfd74d65d832137e20e193c960802afba73b5d38sm    }
154cfd74d65d832137e20e193c960802afba73b5d38sm
155cfd74d65d832137e20e193c960802afba73b5d38sm    public void destroyAll() {
156cfd74d65d832137e20e193c960802afba73b5d38sm        assert mVisitingGraph == false;
157cfd74d65d832137e20e193c960802afba73b5d38sm        commitUpdates();
158cfd74d65d832137e20e193c960802afba73b5d38sm
159cfd74d65d832137e20e193c960802afba73b5d38sm        FixedSizeArray<BaseObject> objects = getObjects();
160cfd74d65d832137e20e193c960802afba73b5d38sm        final int count = objects.getCount();
161cfd74d65d832137e20e193c960802afba73b5d38sm        for (int i = count - 1; i >= 0; i--) {
162cfd74d65d832137e20e193c960802afba73b5d38sm            mMarkedForDeathObjects.add((GameObject)objects.get(i));
163cfd74d65d832137e20e193c960802afba73b5d38sm            objects.remove(i);
164cfd74d65d832137e20e193c960802afba73b5d38sm        }
165cfd74d65d832137e20e193c960802afba73b5d38sm
166cfd74d65d832137e20e193c960802afba73b5d38sm        final int inactiveObjectCount = mInactiveObjects.getCount();
167cfd74d65d832137e20e193c960802afba73b5d38sm        for (int j = inactiveObjectCount - 1; j >= 0; j--) {
168cfd74d65d832137e20e193c960802afba73b5d38sm            mMarkedForDeathObjects.add((GameObject)mInactiveObjects.get(j));
169cfd74d65d832137e20e193c960802afba73b5d38sm            mInactiveObjects.remove(j);
170cfd74d65d832137e20e193c960802afba73b5d38sm        }
171cfd74d65d832137e20e193c960802afba73b5d38sm
172cfd74d65d832137e20e193c960802afba73b5d38sm        mPlayer = null;
173cfd74d65d832137e20e193c960802afba73b5d38sm    }
174cfd74d65d832137e20e193c960802afba73b5d38sm
175cfd74d65d832137e20e193c960802afba73b5d38sm    public void setPlayer(GameObject player) {
176cfd74d65d832137e20e193c960802afba73b5d38sm        mPlayer = player;
177cfd74d65d832137e20e193c960802afba73b5d38sm    }
178cfd74d65d832137e20e193c960802afba73b5d38sm
179cfd74d65d832137e20e193c960802afba73b5d38sm    public GameObject getPlayer() {
180cfd74d65d832137e20e193c960802afba73b5d38sm        return mPlayer;
181cfd74d65d832137e20e193c960802afba73b5d38sm    }
182cfd74d65d832137e20e193c960802afba73b5d38sm
183cfd74d65d832137e20e193c960802afba73b5d38sm    /** Comparator for game objects objects. */
184cfd74d65d832137e20e193c960802afba73b5d38sm    private final static class HorizontalPositionComparator implements Comparator<BaseObject> {
185cfd74d65d832137e20e193c960802afba73b5d38sm        public int compare(BaseObject object1, BaseObject object2) {
186cfd74d65d832137e20e193c960802afba73b5d38sm            int result = 0;
187cfd74d65d832137e20e193c960802afba73b5d38sm            if (object1 == null && object2 != null) {
188cfd74d65d832137e20e193c960802afba73b5d38sm                result = 1;
189cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object1 != null && object2 == null) {
190cfd74d65d832137e20e193c960802afba73b5d38sm                result = -1;
191cfd74d65d832137e20e193c960802afba73b5d38sm            } else if (object1 != null && object2 != null) {
192cfd74d65d832137e20e193c960802afba73b5d38sm                float delta = ((GameObject) object1).getPosition().x
193cfd74d65d832137e20e193c960802afba73b5d38sm                    - ((GameObject) object2).getPosition().x;
194cfd74d65d832137e20e193c960802afba73b5d38sm                if (delta < 0) {
195cfd74d65d832137e20e193c960802afba73b5d38sm                    result = -1;
196cfd74d65d832137e20e193c960802afba73b5d38sm                } else if (delta > 0) {
197cfd74d65d832137e20e193c960802afba73b5d38sm                    result = 1;
198cfd74d65d832137e20e193c960802afba73b5d38sm                }
199cfd74d65d832137e20e193c960802afba73b5d38sm            }
200cfd74d65d832137e20e193c960802afba73b5d38sm            return result;
201cfd74d65d832137e20e193c960802afba73b5d38sm        }
202cfd74d65d832137e20e193c960802afba73b5d38sm    }
203cfd74d65d832137e20e193c960802afba73b5d38sm}
204