Skip to content

Instantly share code, notes, and snippets.

@randomPoison
Created July 16, 2018 02:39
Show Gist options
  • 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

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