⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gameworld.cpp

📁 <B>DirectX9.0 3D游戏编程</B>
💻 CPP
字号:
/*******************************************************************
 *         Advanced 3D Game Programming using DirectX 8.0
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   Title: GameWorld.cpp
 *    Desc: Code to handle the scene, including the cells and
 *          all of the game entities.
 * copyright (c) 2001 by Peter A Walsh and Adrian Perez
 * See license.txt for modification and distribution information
 ******************************************************************/

#include "stdafx.h"
#include "GameWorld.h"
#include "GameCell.h"
#include "Ents\GameEnt.h"
#include "Ents\GamePlayerEnt.h"
#include "Ents\GameSpawnEnt.h"

#include <algorithm>


cGameWorld::cGameWorld( cFile& file )
{
	MsgDaemon()->RegObject( g_gameWorldID, this );

	/**
	 * The first line of the file says how many cells we need to load
	 */
	char buff[1024];
	file.ReadNonCommentedLine( buff, '#' );

	int nCells;
	sscanf( buff, "NUM_CELLS %i", &nCells );

	/**
	 * Read as many cells as we were told to
	 */
	m_nCells = nCells;
	m_pCellList = NULL;
	m_pCellList = new cGameCell* [ m_nCells ];
	int i;
	for( i=0; i<nCells; i++ )
	{
		// defense in case something fails midway and we have to delete the list
		m_pCellList[i] = NULL;
	}

	for( i=0; i<nCells; i++ )
	{
		m_pCellList[i] = new cGameCell( file, i );
	}

	/**
	 * The next line gives us our # of spawn locations
	 * only do this if we're the server.
	 */
	if( !IsClient() )
	{
		int nSpawnLocs;
		file.ReadNonCommentedLine( buff, '#' );
		sscanf( buff, "NUM_SPAWN_LOCS %d", &nSpawnLocs );

		for( i=0; i<nSpawnLocs; i++ )
		{
			point3 spawnLoc;
			file.ReadNonCommentedLine( buff, '#' );
			sscanf( 
				buff, 
				"SPAWN_LOC %f %f %f", 
				&spawnLoc.x,
				&spawnLoc.y,
				&spawnLoc.z );

			LogPrint( "Creating Spawn point at %f %f %f",
				spawnLoc.x, spawnLoc.y, spawnLoc.z );

			sEntState state;
			state.m_status = gesGhost;
			state.m_loc = spawnLoc;

			int spawnID = GetNextSpawnEntID();

			cGameSpawnEnt* pEnt = cGameSpawnEnt::CreateSpawnEnt( 
				spawnID, state );
			m_spawnEnts.push_back( spawnID );
		}
	}

	// Iter is initially the tail of the list
	m_entIter = m_entList.end();
}


cGameWorld::~cGameWorld()
{
	/**
	 * Delete each of the cells in the list
	 */
	if( !m_pCellList )
		return;
	for( int i=0; i<m_nCells; i++ )
	{
		delete m_pCellList[i];
	}
	delete m_pCellList;
}



void cGameWorld::TraverseAll( vector< cGameCell* >* pList )
{
	for( int i=0; i<m_nCells; i++ )
	{
		pList->push_back( m_pCellList[i] );
	}
}



void cGameWorld::Tick( float tickLength )
{
	/**
	 * process each of them.  they'll all take care
	 * of their own physics, take care of notifying
	 * their cell when they die or move elsewhere.
	 * each of them know about the world they live in
	 * and can thusly get info (like objects they should
	 * test against.)
	 */ 
	EntIterStart();
	while( !EntIterDone() )
	{
		EntIterCurr()->Tick( tickLength );
		EntIterNext();
	}
}


objID cGameWorld::FindContainingCell( const point3& loc )
{
	objID parent = -1;
	int i;
	for( i=0; i<m_nCells; i++ )
	{
		if( m_pCellList[i]->ContainsPoint( loc ) )
		{
			parent = m_pCellList[i]->GetID();
			break;
		}
	}
	return parent;
}



cGameCell* cGameWorld::GetCell( int cellNum )
{
	assert( cellNum>=0 && cellNum<m_nCells );
	return m_pCellList[cellNum];
}


void cGameWorld::AddPlayer( objID id )
{
	/**
	 * Note: this adds a player that has already
	 * been created.  the id should be valid.
	 */
	m_players.push_back( (cGamePlayerEnt*)MsgDaemon()->Get( id ) );
}



void cGameWorld::RemovePlayer( objID id )
{
	list< cGamePlayerEnt* >::iterator iter;
	cGamePlayerEnt* pTarget = (cGamePlayerEnt*)MsgDaemon()->Get( id );

	if( !pTarget )
	{
		LogPrint("cGameWorld::RemovePlayer: bad ID");
		return;
	}


	iter = find( m_players.begin(), m_players.end(), pTarget );

	if( iter == m_players.end() )
	{
		DP( "ERROR: Tried to remove non-existent player\n" );
	}
	else
	{
		m_players.erase( iter );
	}

}



void cGameWorld::ValidateObjectMove( objID id )
{
/*	iGameObject* pObj = MsgDaemon()->Get( id );

	// This is the cell before the last physics step
	cGameCell* pOldCell = (cGameCell*)
		MsgDaemon()->Get( 
			pObj->ProcMsg( sMsg( msgGetParent, 0, 0 ) )
		);

	bSphere3 sphere = *(bSphere3*)pObj->ProcMsg( sMsg( msgGetSphere, 0, 0 ) );
	point3 loc = *(point3*)pObj->ProcMsg( sMsg( msgGetLoc, 0, 0 ) );

	cGameCell* pNewCell = NULL;

	bool bStillInOldCell = pOldCell->ContainsPoint( loc );

	if( bStillInOldCell )
	{
		pNewCell = pOldCell;
		// three possible cases:
		//  1: far enough away from the walls, do nothing
		//  2: close to walls and not in a doorway, snap
		//  3: close to walls but in a doorway, do nothing.

		if( pOldCell->ContainsSphere( sphere, true ) )
		{
			// case 1 (do nothing)
		}
		else
		{
			// case 2 or 3
			if( pOldCell->ContainedInPortal( sphere ) )
			{
				// case 3 (do nothing)
			}
			else
			{
				// case 2 (snap)
				pOldCell->SnapInside( (cGameEnt*)pObj );
			}
		}
	}
	else
	{
		// two possible cases:
		//  1: in empty space, snap
		//  2: in a nearby cell, let it deal with it.

		list< cGameCell* > cellList;
		pOldCell->Traverse( &cellList, 2 ); // 2 depth
		cGameCell* pCell;

		// The first cell is pOldCell, don't bother checking it.
		cellList.pop_front();

		while( !pNewCell && !cellList.empty() )
		{
			pCell = *cellList.begin();
			cellList.pop_front();

			if( pCell->ContainsPoint( loc ) )
			{
				pNewCell = pCell;
			}
		}
		
		if( pNewCell )
		{
			// case 2, let the other cell deal with it.
			pObj->m_pCell = pNewCell;
			ValidateObjectMove( id );
			return;
		}
		else
		{
			// case 1, snap it to the old cell
			pNewCell = pOldCell;
			pOldCell->SnapInside( (cGameEnt*)pObj );
		}
	}
*/
	/**
	 * Now we have the cell the object is currently in.
	 * do object collisions.
	 */
/*	sMsg msg;
	msg.m_type = msgSetLoc;
	msg.m_pt = loc;
	pObj->ProcMsg( msg );

	msg.m_type = msgSetParent;
	msg.m_i[0] = pNewCell->GetID();
	pObj->ProcMsg( msg );*/
}


objID cGameWorld::GetID()
{
	return g_gameWorldID;
}



void cGameWorld::SetID( objID id )
{
}



uint cGameWorld::ProcMsg( const sMsg& msg )
{
	switch( msg.m_type )
	{

	case msgFindContainingCell:
		return FindContainingCell( msg.m_pt );

	case msgAddPlayer:
		AddPlayer( msg.m_i[0] );
		return 0;

	case msgRemovePlayer:
		RemovePlayer( msg.m_i[0] );
		return 0;

	case msgAddEnt:
		NotifyAdd(
			(cGameEnt*)MsgDaemon()->Get( msg.m_i[0] ));
		return 0;

	case msgRemoveEnt:
		NotifyRemove(
			(cGameEnt*)MsgDaemon()->Get( msg.m_i[0] ));
		return 0;

	default:
		DP0("Bad Message got to cGameWorld\n");
		return -1;
	}
}




//==========--------------------------  Iteration code

void cGameWorld::EntIterStart()
{
	m_entIter = m_entList.begin();
}

bool cGameWorld::EntIterDone()
{
	return ( m_entIter == m_entList.end() );
}

void cGameWorld::EntIterNext()
{
	m_entIter++;
}

cGameEnt* cGameWorld::EntIterCurr()
{
	return (*m_entIter);
}

void cGameWorld::NotifyAdd( cGameEnt* pEnt )
{
	// Add to the end.
	m_entList.push_back( pEnt );
}

void cGameWorld::NotifyRemove( cGameEnt* pEnt )
{
	LogPrint("[cGameWorld::NotifyRemove] Removing (%d:%d)", 
		GetIDSegment(pEnt->GetID()), GetIDOffset(pEnt->GetID()) );

	tEntList::iterator iter = find(
		m_entList.begin(),
		m_entList.end(),
		pEnt );

	if( iter == m_entList.end() )
	{
		LogPrint( "[cGameWorld::NotifyRemove] object doesn't exist! can't remove." );
	}

	if( iter == m_entIter )
	{
		// erase and adjust the iterator
		m_entIter = m_entList.erase( iter );
	}
	else
	{
		// blindly erase
		m_entList.erase( iter );
	}
}


objID cGameWorld::GetGoodSpawnEnt() 
{
	/**
	 * Step through the list of spawn ents.
	 * choose the one that is farthest from
	 * the rest of the players.
	 */
	list< cGamePlayerEnt* > players;
	ListPlayers( &players );

	DebPrint( "found %d players", players.size() );

	int nSpawnEnts = m_spawnEnts.size();
	int best=0;
	float bestDist = 0.f;

	for( int i=0; i<nSpawnEnts; i++ )
	{
		cGameEnt* pEnt = (cGameEnt*)MsgDaemon()->Get( m_spawnEnts[i] );

		if( !pEnt )
		{
			LogPrint("cGameWorld::GetGoodSpawnEnt: bad ID");
			return -1;
		}


		list< cGamePlayerEnt* >::iterator iter;
		float bestCurrDist = 10000.f;
		for( iter = players.begin(); iter != players.end(); iter++ )
		{
			float currDist = point3::Dist( (*iter)->GetLoc(), pEnt->GetLoc() );
			if( currDist < bestCurrDist )
			{
				bestCurrDist = currDist;
			}
		}
		// If this is the new best one, take it.
		if( bestCurrDist > bestDist )
		{
			best = i;
			bestDist = bestCurrDist;
		}

	}
	// return the best one.
	return m_spawnEnts[ best ];
}


void cGameWorld::RunCollisionTests()
{
	list< cGameEnt* >::iterator iter1, iter2;
	list< cGameEnt* >::iterator temp;
	for( iter1 = m_entList.begin();	iter1 != m_entList.end(); ++iter1 )
	{
		/**
		 * test each object against all of the objects above it.
		 * This is a sucky way to do things ( O(n^2) ).  A better
		 * way would be to test each object only against the list
		 * objects of the nearby cells.  This wouldn't be too hard
		 * to do, but is left as an exercise to the reader.  Anyway,
		 * since n will typically be < 10, we can waste a little time.
		 */

		/**
		 * Early-out if iter1 doesn't hit objects.
		 */
		if( !(*iter1)->HitsObjects() )
		{
			continue;
		}
		
		iter2 = iter1;
		iter2++;
		for( ; iter2 != m_entList.end(); ++iter2 )
		{
			/**
			 * Early-out if iter2 doesn't hit objects.
			 */
			if( !(*iter2)->HitsObjects() )
			{
				continue;
			}

			/**
			 * Do the objects collide?
			 */
			if( bSphere3::Intersect( 
				(bSphere3&)(*iter1)->GetSphere(), 
				(bSphere3&)(*iter2)->GetSphere() ) )
			{
				// handle the collision.
				DebPrint("We had a collision!");
				temp = iter1;
				temp--;
				if( 0 == (*iter1)->RespondToCollision( (*iter2) ) )
				{
					// if we get in here, iter1 destroyed itself as a result
					// of the collision. (a rocket that hit a player, for
					// example).

					// fix iter1 and jump to the next iter1 step.
					iter1 = temp;
					goto iter1Died;
				}

				temp = iter2;
				temp--;
				if( 0 == (*iter2)->RespondToCollision( (*iter1) ) )
				{
					// if we get in here, iter2 destroyed itself as a result
					// of the collision. (a rocket that hit a player, for
					// example).

					// fix iter2 and continue on.
					iter2 = temp;
				}
			}
		}

// We jump to this label when iter1 kills itself in a collision.
iter1Died:

		; // sigh...

	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -