Tuesday, 5 March 2013

Coding progress update

I've finally got a working top-down camera in place. It is controlled by mouse, spawns at the top of the player and follows terrain's elevation to stay at the constant height. It also provides limited zooming in/out functionality with a mouse scroll wheel. Overall works pretty well.

The main problem was the fact that CE3 provides multiple systems for the same tasks (and all of them equally undocumented) E.g. for the input there are high level Action Maps, which use XML to map input types to in game actions, CryInput module which provides device independent input abstraction, and low level hardware access classes. In Game.dll for some reason there are also two camera systems (PlayerView and CameraManager) which seem to do pretty much the same thing (it looks like they were in a process of moving from PlayerView to CameraManager which provides some more functionality). Anyway both of these classes seem to be designed for a player-following camera (1st of 3rd person) and so after a while I gave up trying to modify them for our needs.

What I ended up with is a completely new TopDownCamera class using only low level input and basic ViewParams struct to implement camera functionality. It may not have some of the fancy features of the higher level solutions but it seems enough for our current needs and still much better then flowgraphs. I also think this is the best approach for creating new stuff, i.e. building it from scratch rather than trying to modify existing code.

I'm now going to create a separate class for mouse input to encapsulate some of the functionality I put in the camera class and also add things like on-click feedback and under cursor entity detection.

Don't have any pretty pictures, but here's a piece of code which finds the elevation of the ground under the camera with pretty syntax highlighting. It casts a ray from the middle of the screen into the ground and checks the z value of the point the ray intersects with the terrain.


bool TopDownCamera::getGroundLevel(float& z)
{
 IRenderer* pRenderer = gEnv->pRenderer;

 if(!gEnv->pHardwareMouse || !pRenderer || !gEnv->p3DEngine || !gEnv->pSystem || !gEnv->pEntitySystem || !g_pGame->GetIGameFramework())
  return false;

 IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor();

 if (!pClientActor)
  return false;
  
 int xPos = gEnv->pRenderer->GetWidth() * 0.5;
 int yPos = gEnv->pRenderer->GetHeight() * 0.5;
 
 Vec3 vPos0(0,0,0);
 pRenderer->UnProjectFromScreen((float)xPos, (float)yPos, 0, &vPos0.x, &vPos0.y, &vPos0.z);

 Vec3 vPos1(0,0,0);
 pRenderer->UnProjectFromScreen((float)xPos, (float)yPos, 1, &vPos1.x, &vPos1.y, &vPos1.z);

 const Vec3 vDir = (vPos1-vPos0).GetNormalized();
 
 IPhysicalEntity *pPhysicalEnt = pClientActor->GetEntity() ? pClientActor->GetEntity()->GetPhysics() : NULL;

 if(!pPhysicalEnt)
  return false;

 static IPhysicalEntity* pSkipEnts[2];
 pSkipEnts[0] = pPhysicalEnt;
 int numSkipped = 1;
  
 entity_query_flags queryFlags = ent_terrain;    // see physicsnterface.h for details 
 static const unsigned int flags = rwi_stop_at_pierceable|rwi_colltype_any;
 float  fRange = gEnv->p3DEngine->GetMaxViewDistance();

 static ray_hit hit;

 if (gEnv->pPhysicalWorld && gEnv->pPhysicalWorld->RayWorldIntersection(vPos0, vDir * fRange, queryFlags, flags, &hit, 1, pSkipEnts, numSkipped))
 {
  z = hit.pt.z;
  return true;
 }

 return false;
}

No comments:

Post a Comment