Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Unity.Transforms questions

Discussion in 'Entity Component System' started by andynayrb, Mar 5, 2019.

  1. andynayrb

    andynayrb

    Joined:
    Feb 28, 2019
    Posts:
    25
    So I am pretty new to Unity. Getting a lot of things figured out, at least I think I am getting things figured out. I was a programmer for about 10 years, then I became a high school math teacher. So I haven't programmed a whole lot for the last five years. Recently my son (9) became interested in video games and I thought to myself I wonder if I could make a video game and achieve two goals, spend more time with my son, and teach him a new skill. So I am in the process of leaning Unity so I can then teach him some stuff. So I read about Unity when I am doing my research on development tools, it met my main criteria, it was free. And an added bonus is that I used C# fairly often back in the day. So Unity it is.

    As I am reading about Unity I learn about ECS and some big transitions that Unity is going through. Sounds fun, I think to myself. So I have spent a few weeks getting a pretty good understanding of how ECS is supposed to work. So at this point problems start. So I decide I am going to make a little project (in pure ECS). I understand that there are tons of things missing from ECS (or at least I think their are, namely physics, animations, and other stuff), but I rationalize to myself that it will be fun to try and find work arounds for those missing parts.

    So I decide to just use a cube for now as my "player". I then decide that I am just going to work on taking user input, and in turn, moving my cube all around my 3D space. And largely I have succeeded in that endeavor. I can move it is all different directions, rotate the cube. Well not all different directions. Just up down, side to side, and forward and back. The one direction that I can't get it to go is the direction that the cube if facing. Now there are tons of examples of how to do this, only problem is they are from two or more years ago. And the syntax has changed so much for all the transformation stuff. Maybe this is not where this question should be, seemed reasonable enough to me so there you have it.

    Allegedly this line of code will move my cube in the direction that it is facing, so if I rotate it up and to the left and then press "W" it should move forward up and to the left. But half of it is not valid syntax anymore, so help please. Or if there is a better thing to do I am all ears.

    transform.position += transform.forward * Time.deltaTime * movementSpeed;

    It is my understanding, or misunderstanding, that Physics would be better to use here, but in the absence of that, just trying to learn how to do things.

    One more question, is the goal ultimately to be able to add a Camera to an entity, so it can follow my cube/character around?

    I am getting the big picture, it is just so much syntax is changing so quickly. I have had several things depreciated in the last few weeks. Anyhow it has been lots of fun so far, and the forum is a wealth of knowledge, I would be even more lost without it. So thanks.

    PS I promise I will never write another post that is this long.
     
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,554
    The latest version, LocalToWorld component has Forward property. I think you should try to use that. If you convert your GO to Entity using GameObjectConversionUtility or use RenderMeshProxy for hybrid approach you will get LTW.

    Currently I think you better off using Cinemachine? It can follow any game object. If I have to do it I would make a follow target GO with proxies, then ECS system update the position to be the same as the cube character. Proxy sync back position to Transform, then Cinemachine follow it.
     
  3. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    For your problem i think your will need 3 systems:
    1- Capture player input
    2- Transform player input
    3 - Follow Camera (If you you use Cinemachine like @5argon suggested no need for this)

    Capture system would capture input values and store them in a component (We can use singleton or not depending on how we are going to scale it in the future). We could also use singleton for the Player component
    Code (CSharp):
    1. public class PlayerInputSystem : ComponentSystem {
    2.  
    3.     protected override void OnCreateManager() {
    4.         base.OnCreateManager();
    5.         EntityManager.CreateEntity(typeof(PlayerInput));
    6.         SetSingleton(new PlayerInput());
    7.  
    8.     }
    9.  
    10.     protected override void OnUpdate() {
    11.         var playerInput = GetSingleton<PlayerInput>();
    12.         playerInput.Horizontal = Input.GetAxis("Horizontal");
    13.         playerInput.Vertical = Input.GetAxis("Vertical");
    14.         playerInput.Rotation = Input.GetKey(KeyCode.LeftArrow) ? -1f : Input.GetKey(KeyCode.RightArrow) ? 1f : 0f;
    15.         SetSingleton(playerInput);
    16.     }
    17. }
    Your transform system will move and rotate the player based in the input data:
    Code (CSharp):
    1. [UpdateInGroup(typeof(TransformSystemGroup))]
    2. public class TransformInputSystem : JobComponentSystem {
    3.  
    4.     [BurstCompile]
    5.     struct MoveJob : IJobProcessComponentData<Player, Translation> {
    6.         public PlayerInput Input;
    7.  
    8.         public float DeltaTime;
    9.         public void Execute([ReadOnly]ref Player player, ref Translation translation) {
    10.             translation.Value += new float3(Input.Horizontal, 0, Input.Vertical) * DeltaTime * player.MoveSpeed;
    11.         }
    12.     }
    13.  
    14.     [BurstCompile]
    15.     struct RotateJob : IJobProcessComponentData<Player, Rotation> {
    16.         public PlayerInput Input;
    17.  
    18.         public float DeltaTime;
    19.         public void Execute([ReadOnly]ref Player player, ref Rotation rotation) {
    20.             rotation.Value = math.mul(math.normalize(rotation.Value),
    21.                 quaternion.AxisAngle(math.up(), Input.Rotation * player.RotationSpeed * DeltaTime)); ;
    22.         }
    23.     }
    24.  
    25.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    26.         inputDeps = new MoveJob {
    27.             Input = GetSingleton<PlayerInput>(),
    28.             DeltaTime = Time.deltaTime
    29.         }.Schedule(this, inputDeps);
    30.  
    31.         inputDeps = new RotateJob {
    32.             Input = GetSingleton<PlayerInput>(),
    33.             DeltaTime = Time.deltaTime
    34.         }.Schedule(this, inputDeps);
    35.  
    36.         return inputDeps;
    37.     }
    38. }
    For this to work you need to create a Gameobject in the scene and add:
    RenderMeshProxy
    TranslationProxy
    RotationProxy
    and PlayerProxy

    The Player and PlayerInput components should look something like this:
    Code (CSharp):
    1.  
    2. public struct PlayerInput : IComponentData {
    3.     public float Vertical;
    4.     public float Horizontal;
    5.     public float Rotation;
    6. }
    7.  
    8. [System.Serializable]
    9. public struct Player : IComponentData {
    10.     /// <summary>
    11.     /// Move speed in world units per second
    12.     /// </summary>
    13.     public float MoveSpeed;
    14.  
    15.     /// <summary>
    16.     /// Rotation speed in radians per second
    17.     /// </summary>
    18.     public float RotationSpeed;
    19. }
    20.  
    21. public class PlayerProxy : ComponentDataProxy<Player> { }
    This code requires Unity 2019.2.0a7 with latest Entities package
     
  4. andynayrb

    andynayrb

    Joined:
    Feb 28, 2019
    Posts:
    25
    5argon, as soon as I am able to get everything updated and running properly I will give that a try.

    Gilcat, that is pretty much what I have so far, good to know I am on the right track.
     
  5. andynayrb

    andynayrb

    Joined:
    Feb 28, 2019
    Posts:
    25
    LocalToWorld worked exactly like I wanted it too. Thank you very much.

    I can use a combination of WASD and my mouse to move my mesh Cube around.

    So here is what my PlayerMovementSystem and my PlayerInput component look like. Maybe it can help somebody.

    Code (CSharp):
    1. using Unity.Entities;
    2. using UnityEngine;
    3.  
    4. namespace pure.components
    5. {
    6.     public struct PlayerInput : IComponentData
    7.     {
    8.         public float Horizontal;
    9.         public float Vertical;
    10.         public Vector3 MousePosition;
    11.     }
    12.  
    13. }
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Jobs;
    3. using Unity.Transforms;
    4. using UnityEngine;
    5. using pure.components;
    6.  
    7. namespace pure.systems
    8. {
    9.  
    10.     public class PlayerMovementSystem : JobComponentSystem
    11.     {
    12.         private struct PlayerMovementJob : IJobProcessComponentData<Speed, PlayerInput, Translation, Rotation, LocalToWorld>
    13.         {
    14.             public float DeltaTime;
    15.          
    16.             public void Execute(ref Speed speed, ref PlayerInput input, ref Translation position, ref Rotation rotation, ref LocalToWorld localtoworld)
    17.             {
    18.                 float mouseX;
    19.                 float mouseY;
    20.                 float tiltAngle = 60.0f;
    21.                 float smooth = 5.0f;
    22.              
    23.                 if (input.Horizontal <0)
    24.                 {
    25.                     tiltAngle = 60.0f;
    26.                 }
    27.                 else if (input.Horizontal >0)
    28.                 {
    29.                     tiltAngle = -60.0f;
    30.                 }
    31.                 else tiltAngle = 0.0f;
    32.  
    33.                 mouseX = Mathf.Clamp(input.MousePosition.x, 140.0f, 220.0f);
    34.                 mouseY = Mathf.Clamp(input.MousePosition.y, 140.0f, 220.0f);
    35.  
    36.                 Quaternion target = Quaternion.Euler(mouseY, mouseX, tiltAngle);
    37.  
    38.                 if (input.Vertical > 0)
    39.                 {
    40.                     position.Value += localtoworld.Forward * speed.Value * DeltaTime;
    41.                 }
    42.                 else if (input.Vertical < 0)
    43.                 {
    44.                     position.Value -= localtoworld.Forward * speed.Value * DeltaTime;
    45.                 }
    46.                 rotation.Value = Quaternion.Slerp(rotation.Value, target, DeltaTime * smooth);
    47.             }
    48.         }
    49.  
    50.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    51.         {
    52.             var job = new PlayerMovementJob
    53.             {
    54.                 DeltaTime = Time.deltaTime
    55.             };
    56.  
    57.             return job.Schedule(this, inputDeps);
    58.         }
    59.  
    60.     }
    61. }
     
    GilCat and 5argon like this.
  6. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    Nice, but you can do all of that without LocalToWorld. Also I recommend using the Unity.Mathematics package for all the math involved.
    Last but not least add [BurstCompile] to you job to take advantage of burst.
     
  7. jackalcooper

    jackalcooper

    Joined:
    Mar 25, 2019
    Posts:
    1
    Could you share how you implement the camera follow system? I can't get it working because the camera is not spawned properly by the proxy.
     
  8. schaefsky

    schaefsky

    Joined:
    Aug 11, 2013
    Posts:
    89
    Sorry, was too quick to post. So my first problem is solved with using LocalToWorld.Forward().

    Which leaves me with:
    I saw above that Movement and Rotaion has been split into seperate jobs, is it recommended to do that (splitting on the smallest amount of data)?
    Also above input is done via GetSingleton, SetSingleton, is that the way to go?

    Original post:

    So I had a similar problem, trying to replicate this Unity tutorial in ECS:

    My current problem is that my ship moves forward and backward (line 12) relative to how it started, and not how it is currently rotated:
    Code (CSharp):
    1.  
    2. public class MovementSystem : JobComponentSystem
    3. {
    4.     [BurstCompile]
    5.     struct MovementJob : IJobForEach<Translation, Rotation, MovementInfo>
    6.     {
    7.         public float dt;
    8.         public float rotateSpeed;
    9.  
    10.         public void Execute(ref Translation trans, ref Rotation rotation, [ReadOnly] ref MovementInfo movementInfo)
    11.         {
    12.             trans.Value.z += movementInfo.thrust * dt;
    13.  
    14.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotateSpeed * dt * movementInfo.yaw));
    15.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(new float3(1.0f, 0.0f, 0.0f), rotateSpeed * dt * movementInfo.pitch));
    16.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(new float3(0.0f, 0.0f, 1.0f), rotateSpeed * dt * movementInfo.roll));
    17.         }
    18.     }
    19.  
    20.     protected override JobHandle OnUpdate(JobHandle inputDependencies)
    21.     {
    22.         var job = new MovementJob
    23.         {
    24.             dt = Time.deltaTime,
    25.             rotateSpeed = 10f
    26.         };
    27.         return job.Schedule(this, inputDependencies);
    28.     }
    29. }
     
    Last edited: Jul 21, 2019
  9. shaunnortonAU

    shaunnortonAU

    Joined:
    Jan 19, 2018
    Posts:
    9
    You're just setting the Z of the transform.

    You should set trans.Value = rotation.Value * movementInfo.thrust * dt

    Something like that. Doing Quaternion * Vector makes the resultant vector point towards the rotation.
     
  10. schaefsky

    schaefsky

    Joined:
    Aug 11, 2013
    Posts:
    89
    Have it working like this right now:
    Code (CSharp):
    1.     struct MovementJob : IJobForEach<Translation, Rotation, LocalToWorld, MovementInfo>
    2.     {
    3.         public float dt;
    4.         public float rotateSpeed;
    5.  
    6.         public void Execute(ref Translation trans, ref Rotation rotation, ref LocalToWorld ltw,[ReadOnly] ref MovementInfo movementInfo)
    7.         {
    8.             trans.Value += movementInfo.thrust * dt * ltw.Forward;
    9.  
    10.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotateSpeed * dt * movementInfo.yaw));
    11.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(new float3(1.0f, 0.0f, 0.0f), rotateSpeed * dt * movementInfo.pitch));
    12.             rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(new float3(0.0f, 0.0f, 1.0f), rotateSpeed * dt * movementInfo.roll));
    13.         }
    14.     }