Custom Controller Example: Having an Turret Gun Aim and Fire at the Player
From C4 Engine Wiki
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!
