A few people have asked how they can make network based projectiles.
The kind where you can't just do a raycast like a bullet, as the projectile is much slower and can be dodged by the players or AI. Similar to what you see in space style shooters like the old Wing Commanders / Freelancer, or something like the slower rockets in some FPS games, missiles, etc....
Full credit for this goes to fholm and someone from the old forums who's name I've long since forgotten.
They had some nice videos explaining the issue and showing the accuracy levels after they had implemented this solution.
I've heavily modified what they had posted to suit the requirements we have, such as object pools, DB look ups, etc....but I wont go into any of that here and will try to keep it close to what was originally posted a few years ago.
This solution should work well in many scenarios, but as always may need to be tweaked for some types of weapons or games.
In tests of over 50 bolt entities over a LAN everything works perfectly and looks great, so its a good starting base if you want to achieve the slower moving networked projectiles.
So getting into the details.....
Set-up a bool input on the player command for firing the weapon called fireSelectedWeapon.
And another bool called fireSelectedWeaponAction.
Create an event called PlayerFireProjectile.
Set the global senders to server only, and entity senders to none.
Set the following entity values (name / type)
origin / Vector
rotation / Quaternion
direction / Vector
frame / Integer
entity / Entity
You may also like to add some ID numbers to identify what the type of projectile is, or the prefab, or something similar.
The AI projectile event is very similar, which has the same values, but different IDs to support the different AI weapon types, and has the global senders to server only.
For the movement side of things, you need to set-up the commands like so. The event listener can be entity or global (I think) as it not needed for this at all.
Next up you need something to listen to the events and create the projectiles.
I have one of these on each player game object.
For the AI there is just an overall controller class sitting on a game object in the scene.
This is actually split over several classes, but I've condensed it into one below to show the fire frame bit easier.
The kind where you can't just do a raycast like a bullet, as the projectile is much slower and can be dodged by the players or AI. Similar to what you see in space style shooters like the old Wing Commanders / Freelancer, or something like the slower rockets in some FPS games, missiles, etc....
Full credit for this goes to fholm and someone from the old forums who's name I've long since forgotten.
They had some nice videos explaining the issue and showing the accuracy levels after they had implemented this solution.
I've heavily modified what they had posted to suit the requirements we have, such as object pools, DB look ups, etc....but I wont go into any of that here and will try to keep it close to what was originally posted a few years ago.
This solution should work well in many scenarios, but as always may need to be tweaked for some types of weapons or games.
In tests of over 50 bolt entities over a LAN everything works perfectly and looks great, so its a good starting base if you want to achieve the slower moving networked projectiles.
So getting into the details.....
Set-up a bool input on the player command for firing the weapon called fireSelectedWeapon.
And another bool called fireSelectedWeaponAction.
Create an event called PlayerFireProjectile.
Set the global senders to server only, and entity senders to none.
Set the following entity values (name / type)
origin / Vector
rotation / Quaternion
direction / Vector
frame / Integer
entity / Entity
You may also like to add some ID numbers to identify what the type of projectile is, or the prefab, or something similar.
The AI projectile event is very similar, which has the same values, but different IDs to support the different AI weapon types, and has the global senders to server only.
For the movement side of things, you need to set-up the commands like so. The event listener can be entity or global (I think) as it not needed for this at all.
public class PlayerMovementController : Bolt.EntityEventListener<IPlayerState>
{
// normal movement variables
bool _fireSelectedWeapon;
bool _fireSelectedWeaponAction;
void PollKeys()
{
// other key polls
_fireSelectedWeapon = Input.GetKey("Fire Weapon");
}
public override void SimulateController()
{
PollKeys();
IPlayerCommandInput input = PlayerCommand.Create();
//other movement commands
if ((this._fireSelectedWeapon == true) && (this.CanFire() == true))
{
input.fireSelectedWeaponAction = true;
}
else
{
input.fireSelectedWeaponAction = false;
}
entity.QueueInput(input);
}
public override void ExecuteCommand(Bolt.Command cmd, bool resetState)
{
PlayerShipCommand playerCmd = (PlayerShipCommand)cmd;
if (resetState)
{
//reset state code
}
else
{
//movement code
if (playerCmd.IsFirstExecution)
{
if (playerCmd.Input.fireSelectedWeaponAction)
{
this.FireWeapon(playerCmd);
}
}
}
}
void FireWeapon(PlayerShipCommand cmd)
{
if (BoltNetwork.isServer == true)
{
var ev = PlayerFireProjectile.Create();
ev.entity = entity;
ev.origin = this.transform.position;
ev.rotation = this.transform.rotation;
ev.direction = // the the velocity from your player motor to add the players velocity to the projectile, if you want to do this
ev.frame = cmd.ServerFrame;
ev.Send();
}
}
private bool CanFire()
{
return true; // if the weapon can be fired based on cooldowns, energy, etc...
}
}
Next up you need something to listen to the events and create the projectiles.
I have one of these on each player game object.
For the AI there is just an overall controller class sitting on a game object in the scene.
This is actually split over several classes, but I've condensed it into one below to show the fire frame bit easier.
public class PlayerWeaponsController : Bolt.GlobalEventListener
{
private BoltEntity _entity;
public int fireFrame
{
get;
set;
}
public override void OnEvent(PlayerFireProjectile evnt)
{
if(evnt.entity == _entity)
this.FireEvent(evnt);
}
private void FireEvent(PlayerFireProjectile evnt)
{
this.FireWeapon(evnt.weaponsGroup, evnt.mountPointID, evnt.frame, evnt.origin, evnt.rotation, evnt.direction);
}
private void FireWeapon(int frame, Vector3 position, Quaternion rotation, Vector3 direction)
{
//Set the weapon cool down fame
this.fireFrame = frame;
// Get the projectile from an object pool
GameObject obj = ObjectPool.instance.GetObjectForType(_ammoPrefabName);
obj.GetComponent<AmmoBase>().Init(frame, position, rotation, direction);
obj.GetComponent<AmmoBase>().IgnoreHitbox(transform.gameObject.GetComponents<BoltHitbox>());
obj.SetActive(true);
}
}