360 likes | 484 Views
Sage Demo 3 Objects. SAGE Lecture Notes Ian Parberry University of North Texas. SAGE Demo 3. The goal of this demo is to add four types of objects into the game A plane A crow A silo A windmill. Key Topics. Game Objects Game Object Management Generators. Game Objects Overview.
E N D
Sage Demo 3Objects SAGE Lecture Notes Ian Parberry University of North Texas
SAGE Demo 3 • The goal of this demo is to add four types of objects into the game • A plane • A crow • A silo • A windmill
Key Topics • Game Objects • Game Object Management • Generators
Game Objects Overview • Base Object – Defines an interface for creating new objects, and manages some of the features • Derived Objects – These are the actually objects. They inherit the base object and implement its classes
Base Game Object • Parts – The game objects are split into 1 or more parts. This allows you to animate individual parts of a model • Example: The plane is made up of two parts, the body and the propellers. This is done so that the propellers can spin independently of the plane
Base Game Object Cont. • Process and Move – Virtual functions used to handle the positioning and moving of an object • process() – Implement non-movement logic by overriding the process function • AI is a good example of what to do here • move() – Should handle the updating of the objects position • The move functions handles normal forward motion for you, but you’ll want to override it if you want to handle more complex movement such as strafing void GameObject::move(float dt, bool savePreviousState)
Base Game Object Cont. • move() Cont – The default implementation performs the following functions • Saves the previous state • Sets the orientation and displacement if(savePreviousState) { m_oldPosition = m_v3Position[0]; m_oldOrient = m_eaOrient[0]; } //orientation float rotStep = dt; for(int i=0; i<m_nNumParts; i++){ m_eaOrient[i].heading += m_eaAngularVelocity[i].heading * rotStep; m_eaOrient[i].pitch += m_eaAngularVelocity[i].pitch * rotStep; m_eaOrient[i].bank += m_eaAngularVelocity[i].bank * rotStep; } //displacement Vector3 bDisplacement(0, 0, 20.0f * dt * m_fSpeed); RotationMatrix Matrix; Matrix.setup(m_eaOrient[0]); Vector3 addend = Matrix.objectToInertial(bDisplacement); m_v3Position[0] += Matrix.objectToInertial(bDisplacement);
Base Game Object Cont. • move() Cont – The default implementation performs the following functions • Animates the object if there is more then one frame if(m_nNumFrames > 1) { m_fCurFrame += dt * ((AnimatedModel*)m_pModel)->numFramesInAnimation() * m_animFreq; Matrix4x3 world, modelOrient; world.setupLocalToParent(m_v3Position[0], m_eaOrient[0]); modelOrient.setupLocalToParent(Vector3::kZeroVector,m_modelOrient); world = modelOrient * world; ((AnimatedModel*)m_pModel)->selectAnimationFrame(m_fCurFrame, 0, *m_vertexBuffer, m_boundingBox, world); }
Base Game Object Cont. • render() – Handles basic rendering of the object based on the following assumptions • If there is no model associated with the object, nothing is drawn • If there are multiple parts to the object, the object is assumed to be articulated • If there are multiple frames associated with the object, it is assumed that the object is animated • If these assumptions don’t work for you, then you need to override the render function with your own functionality. if (m_nNumParts > 1)//articulated model ((ArticulatedModel*)m_pModel)->renderSubmodel(0); else if (m_nNumFrames > 1)// animated model ((AnimatedModel*)m_pModel)->render(m_vertexBuffer); else m_pModel->render(); //vanilla model for (int i=1; i<m_nNumParts; i++){ gRenderer.instance(m_v3Position[i], m_eaOrient[i]); ((ArticulatedModel*)m_pModel)->renderSubmodel(i); gRenderer.instancePop(); // submodel i }
Base Game Object Cont. • AABB – At any time, you can query the object for its bounding box. AABBs will be covered in the next demo • Name and ID – You can set the name and id of an object manually or you can let the object manager do it for you • Note: Every object must have a unique id and a unique name • The name and ID Generators will be covered later
Base Game Object Cont. • Type (Class ID) – Identifies the type/class of an object at runtime. The values shown in the following enumerations can be assigned to an objects m_Type value to specify its type. • Life State – Used by the object manager to manager the object. namespace ObjectTypes { enum ObjectType { PLANE = 0, CROW, TERRAIN, WATER, SILO, WINDMILL }; };
Derived Game Objects • The following derived objects are currently implemented in the Ned3D game. • Silo – Static game object otherwise known as furniture • process() – Overwritten to do nothing since the silo does nothing • move() – Calls the base move class to put the silo in its position • Windmill – Same as the Silo except one key difference. The fan on the windmill moves • Crow – A moveable game object • process() – Overwritten to determine the flight path of the crow • move() – Overwritten to move the crow along its flight path
Derived Game Objects Cont. • Silo – Static game object otherwise known as furniture • process() – Overwritten to do nothing since the silo does nothing • move() – Calls the base move class to put the silo in its position • Windmill – Same as the Silo except one key difference. The fan on the windmill moves
Derived Game Objects Cont. • Crow – Moves around the screen using simple AI and uses an animated model • move() – The move functions has been overridden to implement the flight path for the crow. • Circular flight path • Straight flight path – Requires only a call to the move() base class Vector3 right(m_v3Position[0].x - m_circleCenter.x, m_circleCenter.y, m_v3Position[0].z - m_circleCenter.z); const Vector3 &up = Vector3::kUpVector; Vector3 forward = up.crossProduct(right); if(!m_circleLeft) { forward *= -1.0f; right *= -1.0f; }
Derived Game Objects Cont. • Plane – The plane needs to able to respond to input in order to perform the following actions • Turn Left • Turn Right • Climb • Dive • Change Speed • Each of these actions is handled by a separate function
Derived Game Objects Cont. • Plane Cont. – Three other functions exists to handle the planes movement. • inputStraight – Straightens out the plane • inputLevel – Levels out the plane • inputStop – Stops the plane
Derived Game Objects Cont. • Plane Cont. • process() – Checks for keyboard and joystick input and process it before passing the data along to the move function • move() – Updates the planes position based on the information saved by the process function. switch(m_turnState) { case TS_LEFT: { planeOrient.heading -= m_maxTurnRate * m_turnRate * dt; if(planeOrient.bank < kPi * 0.25f) planeOrient.bank += m_maxBankRate * m_turnRate * dt; } break; case TS_RIGHT: { planeOrient.heading += m_maxTurnRate * m_turnRate * dt; if(planeOrient.bank > kPi * -0.25f) planeOrient.bank -= m_maxBankRate * m_turnRate * dt; } }
Derived Game Objects Cont. • Special Objects – The terrain and water objects don’t benefit much from being objects, but it lets object manager manage them and results in a cleaner design.
Base Object Manager • The base object manager performs the following functions • Add/Remove objects • Process/Move objects • Render objects • Track objects
Add/Remove Objects • addObject() – Adds an object to the manager using the following parameters • GameObject *object – A pointer to the object being added • bool canMove – Specifies whether this is a movable object • bool canProcess – Specifies whether this object should be processed • bool canRender – Specifies whether this object should be rendered • std::string* name – The name given to the object unsignedint GameObjectManager::addObject(GameObject *object, bool canMove, bool canProcess, bool canRender, const std::string *name)
Add/Remove Objects Cont. • addObject() Cont. • Inserts the object into the process lists based on the Booleans specified unsignedint GameObjectManager::addObject(GameObject *object, bool canMove, bool canProcess, bool canRender, const std::string *name) { assert(object != NULL); if(object->m_manager == this) return object->m_id; assert(object->m_manager == NULL); object->m_manager = this; m_objects.insert(object); if(canMove) m_movableObjects.insert(object); if(canProcess) m_processableObjects.insert(object); if(canRender) m_renderableObjects.insert(object);
Add/Remove Objects Cont. • addObject() Cont. • Checks if there is a name, and creates one if there isn’t, otherwise it checks for a conflicting name. If a conflicting name is found, a name generator determines a new name for it based on the name given. • Return the unique unsigned integer ID assigned to the object by the object manager if(name == NULL || name->length() == 0) object->m_name = m_objectNames.generateName(object->m_className); elseif(m_objectNames.requestName(*name)) object->m_name = *name; // Requested name accepted else object->m_name = m_objectNames.generateName(*name); // Append number to requested name // Ensure new object status object->m_lifeState = GameObject::LS_NEW; // Add id and name mappings m_nameToID[object->m_name] = object->m_id; m_idToObject[object->m_id] = object; return object->m_id; }
Add/Remove Objects Cont. • deleteObject() – Removes the object from the object manager void GameObjectManager::deleteObject(GameObject *object) { if(object == NULL) return; m_nameToID.erase(object->m_name); m_idToObject.erase(object->m_id); m_objectIDs.releaseID(object->m_id); m_objectNames.releaseName(object->m_name); m_objects.erase(object); m_movableObjects.erase(object); m_processableObjects.erase(object); m_renderableObjects.erase(object); object->m_manager = NULL; delete object; }
Ned3D Object Manager • The Ned3DObjectManager is derived from the base object manager, and is responsible for spawning the specific objects in Ned3D using the following functions • spawnPlane() • spawnCrow() • spawnSilo() • spawnWindmill() • spawnWater() • spawnTerrain()
spawnPlane unsigned int Ned3DObjectManager::spawnPlane( const Vector3 &position, const EulerAngles &orientation) { if(m_plane != NULL) return 0;// Only one plane allowed if (m_planeModel == NULL) m_planeModel = m_models->getModelPointer("Plane");// Cache plane model if (m_planeModel == NULL) return 0;// Still NULL? No such model m_plane = new PlaneObject(m_planeModel); m_plane->setPosition(position); m_plane->setOrientation(orientation); unsigned int id = addObject(m_plane, "Plane"); return id; }
spawnCrow • There are two spawn crow functions, one to spawn a straight flying crow, and one to spawn a circling crow. unsigned int Ned3DObjectManager::spawnCrow(const Vector3 &position, const EulerAngles &orientation, float speed) { if(m_crowModel == NULL) m_crowModel = m_models->getModelPointer("Crow");// Cache crow model if(m_crowModel == NULL) return 0; // Still NULL? No such model CrowObject *crow =new CrowObject(m_crowModel); crow->setSpeed(speed); crow->setPosition(position); crow->setOrientation(orientation); crow->setMovementPattern(CrowObject::MP_STRAIGHT); unsigned int id = addObject(crow); m_crows.insert(crow); return id; }
spawnTerrain/Water unsigned int Ned3DObjectManager::spawnTerrain(Terrain *terrain) { m_terrain = new TerrainObject(terrain); return addObject(m_terrain, false, false, false, "Terrain"); } unsigned int Ned3DObjectManager::spawnWater(Water *water) { m_water = new WaterObject(water); return addObject(m_water, false, false, false, "Water"); }
spawnSilo unsigned int Ned3DObjectManager::spawnSilo(const Vector3 &position, const EulerAngles &orientation) { static const std::string silos[] = {"Silo1","Silo2","Silo3","Silo4"}; static int whichSilo = 0; m_siloModel = m_models->getModelPointer(silos[whichSilo]);// Cache silo model if (m_siloModel == NULL) return 0;// Still NULL? No such model SiloObject *silo =new SiloObject(m_siloModel); silo->setPosition(position); silo->setOrientation(orientation); unsigned int id = addObject(silo); m_furniture.insert(silo); whichSilo = ++whichSilo % 4; return id; }
spawnWindmill unsigned int Ned3DObjectManager::spawnWindmill( const Vector3 &position, const EulerAngles &orientation) { if (m_windmillModel == NULL) m_windmillModel = m_models->getModelPointer("Windmill");// Cache windmill if (m_windmillModel == NULL) return 0;// Still NULL? No such model WindmillObject *windmill =new WindmillObject(m_windmillModel); windmill->setPosition(position); windmill->setPosition(Vector3(0,27.0f,-0.5f),1); windmill->setRotationSpeedBank(kPiOver2,1); windmill->setOrientation(orientation); unsigned int id = addObject(windmill); m_furniture.insert(windmill); return id; }
Tether Camera • When a camera is made to follow specific object, it is called a tether camera • Our tether camera is made to always be the same distance from any object • The process() functions handles the positioning of the camera based on the position of the object it is attached to
Tether Camera Cont. • process() • First, we get the position of our targeted object • We increment the y value by 3.0 to keep the object from blocking our view of the rest of the screen targetHeading = obj->getOrientation().heading; target = obj->getPosition(); target.y += 3.0f;
Tether Camera Cont. • process() Cont. • Next we compute the vector from the camera to the target and the distance of that vector • Using this information, we can compute the cameras position // Compute current vector to target Vector3 iDelta = target - cameraPos; // Compute current distance float dist = iDelta.magnitude(); // Get normalized direction vector Vector3 iDir = iDelta / dist;
Tether Camera Cont. • process() Cont. • Now we clamp the distance within our limits and compute the camera’s position • Then we just need to re-compute the camera heading, pitch, and bank // Clamp within desired range to get distance // for next frame if (dist < minDist) dist = minDist; if (dist > maxDist) dist = maxDist; // Recompute difference vector iDelta = iDir * dist; // Compute camera's position cameraPos = target - iDelta; // Compute heading/pitch to look in given direction cameraOrient.heading = atan2(iDir.x, iDir.z); cameraOrient.pitch = -asin(iDir.y); cameraOrient.bank = 0.0f;
Generators • The generators are used to create unique identifiers and names for the objects in the game • There are two generators used in the SAGE engine • ID Generator – Generates a unique identifier. These numbers are stored in a hash set to make it easier to determine when numbers have already been used • Name Generator – Generates a unique name based on a base name given to the generator • Ex: Every time the name generator is asked to generate a name, it appends a number to the end of that name. A counter is kept for all the name bases to keep these numbers unique. • So, if you generated 4 objects with the name crow, their names would be “Crow”, “Crow1”, “Crow2”, and “Crow3”