Custom Controller Example: Having an Turret Gun Aim and Fire at the Player

From C4 Engine Wiki

Jump to: navigation, search

Getting a Turret Gun to always Face and then Shoot at the Player

In this example, the controller checks where the player is. On some distance or other conditions it then updates the node (say turret gun) to face and fire at the player. The turret rotates slowly to the player - the interpolation speed is hard-coded in this version below. Note that this was last tested with Builds 145 and 146.

Thanks to JamesH for the code for getting the local players position!

Let me know if you have any questions about it.

--Jimbobjames 04 February 2008

void SpinController::Move(void)
{
	World *world = TheWorldMgr->GetWorld();
	// Calculate the new spin angle based on how much time has passed
	float angle = spinAngle + spinRate * TheTimeMgr->GetFloatDeltaTime();
 
	if (angle > 100.0F) angle -= 200.0F;
	else if (angle < -100.0F) angle += 200.0F;
 
	spinAngle = angle;
 
	Node *target = GetTargetNode();
 
        //You could also search for a subnode of the Entity - eg just the barrel of the gun would then move.
	//Entity *myEntity = (Entity *)GetTargetNode();
        //Node *target = myEntity->FindNode("Barrel");
 
	const GamePlayer *player = static_cast<const GamePlayer *>(TheMessageMgr->GetLocalPlayer());
	FighterController *controller = player->GetPlayerController();
	Point3D point = controller->GetColliderPosition();
 
	Vector3D view = (point - target->GetNodePosition());
 
	float x = view.x;
	float y = view.y;
	float z = view.z;
	float distance = Sqrt(x * x + y * y + z * z);
 
	view = view.Normalize();
 
	if (distance < 50.0f) {// && view.z >=0.0f) {//ie if also above turret gun
 
        //interpolate between the current viewing direction and the target viewing direction. 
        //If you use say 40.0 instead of 20.0 then it will move twice as slowly.
	Vector3D updatedView = Vector3D(originalView.x+(view.x-originalView.x)/20.0f,originalView.y+(view.y-originalView.y)/20.0f,originalView.z+(view.z-originalView.z)/20.0f);
 
	originalView = updatedView;
 
	x = updatedView.x;
	y = updatedView.y;
 
	float f = InverseSqrt(x * x + y * y);
	Vector3D right(y * f, -x * f, 0.0F);
 
	Vector3D down = updatedView % right;
	target->SetNodeMatrix3D(updatedView, -right, -down);
 
	Point3D startPos = target->GetNodePosition();
 
	//you dont want the ammo to start inside the enemy - this is just in front of the enemy
	//you can vary 4.0f to be say 1.5f or 3.0f, depending on your model
        //better still would be to use a marker on the model to be the point where the projectile exits (-> next version!!)
	startPos.x += 4.0f * updatedView.x;
	startPos.y += 4.0f * updatedView.y;
	startPos.z += 4.0f * updatedView.z;
 
        //The projectile velocity is reduced by a factor of 15.0f here to slow it down
	//otherwise the player has no chance at all!
	Point3D objectVelocity = Point3D(updatedView.x/15.0f, updatedView.y/15.0f, updatedView.z/15.0f);
 
	//Firing
	if (world)
	{
	  Controller	*controller;	
	  Entity *entity = nullptr;
 
	  //Fire on some condition - here is just an example for a quick test
          //So this is true about once every few seconds - increase (or decrease) "40" 
          //for longer (or shorter) pauses between shots
	  if ((((int)angle) % 40) == 0) //angle > -60.0F && angle < -0.0F && 
	  {
		//create fireball!!
		controller = new FireballController(startPos, objectVelocity);
		entity = Entity::Get(kEntityFireball);
 
		if (entity)
		{
			entity->SetController(controller);
 
			Zone *zone = world->FindZone(startPos);
 
			Point3D zonePosition = zone->GetInverseWorldTransform() * startPos;
			entity->SetNodePosition(zonePosition);
			zone->AddNewSubnode(entity);
			entity->Update();
 
			controller->EnterWorld(zone, zonePosition);
		}
	  }
    }
    // Invalidate the target node so that it gets updated properly
    target->Invalidate();
 
  }//if distance
 
}

Note that your Preprocess() method will need code like this:

    // Grab the original transform of the target node
    const Node *target = GetTargetNode();
    originalTransform = target->GetNodeTransform();

    Point3D originalDirection = Point3D(-originalTransform[1].x,-originalTransform[1].y,-originalTransform[1].z);
    originalView = (originalDirection).Normalize();

Remember to declare originalView as Vector3D and originalTransform as a Transform4D in MGControllers.h

        Transform4D   originalTransform;    // The turrets's original transform - set in the PreProcess() method
	Vector3D      originalView;	    // The direction turret is facing before updating each frame

Have fun with this!

Personal tools