Skip to content

Instantly share code, notes, and snippets.

@randomPoison
Created July 16, 2018 02:39
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save randomPoison/f3c08206c5055e3c746d8b14b20721d6 to your computer and use it in GitHub Desktop.
Save randomPoison/f3c08206c5055e3c746d8b14b20721d6 to your computer and use it in GitHub Desktop.
Comparison of writing a system with Unity's new ECS vs writing one in Amethyst.
use ::{FrameId, WriteConnection};
use amethyst::ecs::prelude::{Read, System};
use amethyst::input::InputHandler;
use core::{
ClientMessage,
ClientMessageBody,
InputFrame,
math::*,
};
#[derive(Debug)]
pub struct PlayerInputSystem;
#[derive(SystemData)]
pub struct Data<'s> {
input: Read<'s, InputHandler<String, String>>,
connection: WriteConnection<'s>,
frame_id: Read<'s, FrameId>,
}
impl<'s> System<'s> for PlayerInputSystem {
type SystemData = Data<'s>;
fn run(&mut self, mut data: Self::SystemData) {
let forward_backward = data.input.axis_value("forward_backward").expect("forward_backward axis not found");
let left_right = data.input.axis_value("left_right").expect("forward_backward axis not found");
let input = InputFrame {
// TODO: Is it a good idea to downcast from `f64` here? Would it make more sense to keep the input as `f32`?
movement_dir: Vector2::new(left_right as f32, forward_backward as f32),
yaw_delta: 0.0,
pitch_delta: 0.0,
revolver_actions: Vec::new(),
};
// TODO: Send the real input state to the server.
data.connection.send(ClientMessage {
frame: data.frame_id.0,
body: ClientMessageBody::Input(input),
});
}
}
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
using Unity.Transforms2D;
namespace TwoStickPureExample
{
public class PlayerMoveSystem : ComponentSystem
{
public struct Data
{
public readonly int Length;
public ComponentDataArray<Position2D> Position;
public ComponentDataArray<Heading2D> Heading;
public ComponentDataArray<PlayerInput> Input;
}
[Inject] private Data m_Data;
protected override void OnUpdate()
{
var settings = TwoStickBootstrap.Settings;
float dt = Time.deltaTime;
for (int index = 0; index < m_Data.Length; ++index)
{
var position = m_Data.Position[index].Value;
var heading = m_Data.Heading[index].Value;
var playerInput = m_Data.Input[index];
position += dt * playerInput.Move * settings.playerMoveSpeed;
if (playerInput.Fire)
{
heading = math.normalize(playerInput.Shoot);
playerInput.FireCooldown = settings.playerFireCoolDown;
PostUpdateCommands.CreateEntity(TwoStickBootstrap.ShotSpawnArchetype);
PostUpdateCommands.SetComponent(new ShotSpawnData
{
Shot = new Shot
{
TimeToLive = settings.bulletTimeToLive,
Energy = settings.playerShotEnergy,
},
Position = new Position2D{ Value = position },
Heading = new Heading2D{ Value = heading },
Faction = Factions.kPlayer,
});
}
m_Data.Position[index] = new Position2D {Value = position};
m_Data.Heading[index] = new Heading2D {Value = heading};
m_Data.Input[index] = playerInput;
}
}
}
}
@randomPoison
Copy link
Author

This gist compares writing a system in Unity (with the preview version of Unity's ECS) to writing one in Amethyst (using specs). The key similarities to note:

  • You define a system as a new struct that implements a specific interface (the System trait in Amethyst, inheriting from ComponentSystem in Unity).
  • Systems need to declare their input data statically using a helper type (the Data struct in both examples).
  • The game engine injects the necessary data into the system when it is executed.

@randomPoison
Copy link
Author

Another similarity that I've noted is that both Unity and Specs provide a way to parallelize iteration of components within a system. It looks like Unity's version is a bit more thought out at this point (it breaks components into chunks of several thousand and those chunks can be processed in parallel, whereas it seems like specs tries to parallelize each component), but the core structure of "systems are run in parallel, and then iteration over components within a system can also proceed in parallel" is largely the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment