The question is answered, right answer was accepted
Unity Pure ECS Physics Acting Weird Help!!
I wanted to create a character controller using pure ECS and new unity physics system. I created the Dynamic Character and a Static Floor following this documentation and they collide well. But when i move the player, some weird physics things happens, the capsule collider starts wobbling up & down. Here's the Video showing what i mean. I have been working to solve this from 1 day and now i need your help Guys!
Here's my PlayerManager Script Which i use to instantiate entities:
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Rendering;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.Rendering;
using BoxCollider = Unity.Physics.BoxCollider;
using CapsuleCollider = Unity.Physics.CapsuleCollider;
using Collider = Unity.Physics.Collider;
using Material = UnityEngine.Material;
public class PlayerManager : MonoBehaviour
{
public static PlayerManager instance;
public float moveSpeed, jumpForce, maxPlayerSpeed;
public float groundCheckDistance = 0.1f;
public Mesh playerMesh;
public float playerCapsuleRadius = 0.5f, playerCapsuleHeight = 2f;
public Material playerMaterial;
public float3 playerSpawnPos = new float3(1f, 0.5f, -1.8f);
public float3 playerSize = new float3(0.5f, 0.5f, 0.5f);
public Mesh floorMesh;
public Material floorMaterial;
public float3 floorSize = new float3(5f, 0.15f, 5f);
public float3 floorSpawnPosition = new float3(0f, 0f, 0f);
public float3 floorColliderCenter = new float3(0.67f, -0.075f,1.16f);
[HideInInspector]
public RigidBody playerRigidBody;
public unsafe Entity CreateBody(RenderMesh displayMesh, float3 position, quaternion orientation, float3 scale,
BlobAssetReference<Collider> collider,
float3 linearVelocity, float3 angularVelocity, float mass, bool isDynamic)
{
var entityManager = World.Active.EntityManager;
var componentTypes = new ComponentType[isDynamic ? 8 : 5];
componentTypes[0] = typeof(RenderMesh);
componentTypes[1] = typeof(TranslationProxy);
componentTypes[2] = typeof(RotationProxy);
componentTypes[3] = typeof(PhysicsCollider);
componentTypes[4] = typeof(LocalToWorld);
if (isDynamic)
{
componentTypes[5] = typeof(PhysicsVelocity);
componentTypes[6] = typeof(PhysicsMass);
componentTypes[7] = typeof(PhysicsDamping);
}
var entity = entityManager.CreateEntity(componentTypes);
entityManager.SetSharedComponentData(entity, displayMesh);
entityManager.AddComponentData(entity, new Translation() {Value = position});
entityManager.AddComponentData(entity, new Rotation() {Value = orientation});
entityManager.AddComponentData(entity, new Scale() {Value = playerSize.x});
entityManager.SetComponentData(entity, new LocalToWorld() {Value = float4x4.TRS(position, orientation, scale)});
entityManager.SetComponentData(entity, new PhysicsCollider() {Value = collider});
if (isDynamic)
{
var colliderPtr = (Collider*) collider.GetUnsafePtr();
entityManager.SetComponentData(entity, PhysicsMass.CreateDynamic(colliderPtr->MassProperties, mass));
playerRigidBody = new RigidBody
{
WorldFromBody = new RigidTransform(orientation, position),
Collider = colliderPtr,
Entity = entity,
CustomTags = 0
};
var angularVelocityLocal =
math.mul(math.inverse(colliderPtr->MassProperties.MassDistribution.Transform.rot), angularVelocity);
entityManager.SetComponentData(entity, new PhysicsVelocity()
{
Linear = linearVelocity,
Angular = angularVelocityLocal
});
entityManager.SetComponentData(entity, new PhysicsDamping()
{
Linear = 0.01f,
Angular = 0.05f
});
}
return entity;
}
public Entity CreateDynamicCapsule(RenderMesh displayMesh, float radius, float height, float3 position, quaternion orientation)
{
var filter = new CollisionFilter()
{
BelongsTo = 1u,
CollidesWith = (uint) (1 << 1),
GroupIndex = 1
};
// Capsule with default filter and material. Add to Create() call if you want non default:
var capsuleCollider =
CapsuleCollider.Create(
new CapsuleGeometry
{
Radius = radius, Vertex0 = new float3(position.x, position.y + height / 2f, position.z),
Vertex1 = new float3(position.x, position.y - height / 2f, position.z)
},
filter);
return CreateBody(displayMesh, position, orientation, playerSize, capsuleCollider, float3.zero, float3.zero, 1.0f, true);
}
public unsafe Entity CreateStaticBody(RenderMesh displayMesh, float3 position, quaternion orientation,
BlobAssetReference<Collider> collider,
float3 linearVelocity, float3 angularVelocity, float mass, float3 scale)
{
var entityManager = World.Active.EntityManager;
var componentTypes = new ComponentType[5];
componentTypes[0] = typeof(RenderMesh);
componentTypes[1] = typeof(TranslationProxy);
componentTypes[2] = typeof(RotationProxy);
componentTypes[3] = typeof(PhysicsCollider);
componentTypes[4] = typeof(LocalToWorld);
var entity = entityManager.CreateEntity(componentTypes);
entityManager.SetSharedComponentData(entity, displayMesh);
entityManager.AddComponentData(entity, new Translation() {Value = position});
entityManager.AddComponentData(entity, new Rotation() {Value = orientation});
entityManager.AddComponentData(entity, new NonUniformScale() {Value = new float3(1f, 1f, 1f)});
entityManager.SetComponentData(entity, new LocalToWorld() {Value = float4x4.TRS(position, orientation, scale)});
entityManager.SetComponentData(entity, new PhysicsCollider() {Value = collider});
return entity;
}
public Entity CreateStaticBox(RenderMesh displayMesh, float3 centerOffset, float3 size, float3 position, quaternion orientation)
{
var filter = new CollisionFilter()
{
BelongsTo = (uint) (1 << 1),
CollidesWith = 1u,
GroupIndex = 1
};
// Box with default filter and material. Add to Create() call if you want non default:
var boxCollider =
BoxCollider.Create(
new BoxGeometry() {BevelRadius = 0.1f, Center = position + centerOffset, Orientation = quaternion.identity, Size = size},
filter);
return CreateStaticBody(displayMesh, position, orientation, boxCollider, float3.zero, float3.zero, 1.0f, new float3(1f, 1f,1f));
}
// Start is called before the first frame update
private void Start()
{
instance = this;
var pMesh = new RenderMesh()
{
castShadows = ShadowCastingMode.On,
layer = 1,
material = playerMaterial,
mesh = playerMesh,
receiveShadows = true,
subMesh = 0
};
var playerEntity = CreateDynamicCapsule(pMesh, playerCapsuleRadius, playerCapsuleHeight, playerSpawnPos,
quaternion.identity);
var entityMan = World.Active.EntityManager;
entityMan.AddComponent(playerEntity, typeof(PurePlayerInput));
entityMan.AddComponent(playerEntity, typeof(PurePlayerMovement));
var fMesh = new RenderMesh()
{
castShadows = ShadowCastingMode.On,
layer = 1,
material = floorMaterial,
mesh = floorMesh,
receiveShadows = true,
subMesh = 0
};
var floorEntity = CreateStaticBox(fMesh, floorColliderCenter, floorSize, floorSpawnPosition, quaternion.identity);
}
}
My PlayerMovementSystem Script:
using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Extensions;
using Unity.Transforms;
using UnityEngine;
using Math = System.Math;
[UpdateAfter(typeof(PurePlayerInputSystem))]
public class PurePlayerMovementSystem : JobComponentSystem
{
public struct PlayerMovementJob : IJobForEach<PurePlayerInput, PurePlayerMovement, PhysicsVelocity, PhysicsMass, Translation, Rotation>
{
public float deltaTime;
public bool jumpPressed;
public float moveSpeed, jumpForce, maxPlayerSpeed;
public float groundCheckDistance;
private bool isJumping, changeOfDirection;
private float prevInputX, prevInputY;
private bool IsGrounded(float3 from, float3 to)
{
var filter = new CollisionFilter()
{
BelongsTo = (uint) (1 << 2),
CollidesWith = (uint) (1 << 1),
GroupIndex = 0
};
var input = new RaycastInput()
{
End = to,
Filter = filter,
Start = from
};
if (PlayerManager.instance == null || PlayerManager.instance.playerRigidBody.Equals(null))
return false;
var hit = PlayerManager.instance.playerRigidBody.CastRay(input);
Debug.Log("Is Grounded: " + hit);
return hit;
}
[BurstCompile]
public void Execute(ref PurePlayerInput input, ref PurePlayerMovement movement, ref PhysicsVelocity vel, ref PhysicsMass mass,
ref Translation translation, ref Rotation rotation)
{
movement.jumpForce = jumpForce;
movement.moveSpeed = moveSpeed;
rotation.Value = quaternion.identity;
if (jumpPressed && !isJumping && IsGrounded(translation.Value, translation.Value - (translation.Value / 2f - groundCheckDistance)))
{
vel.Linear = float3.zero;
vel.Angular = float3.zero;
var impulse = new float3(0f, jumpForce, 0f);
vel.ApplyLinearImpulse(mass, impulse);
}
if (Math.Abs(vel.Linear.y) > 0.5f)
isJumping = true;
else
isJumping = false;
if (input.InputX != 0 || input.InputY != 0)
{
//Player Input Given
changeOfDirection = false;
if ((input.InputX > 0 && prevInputX < 0) || (input.InputX < 0 && prevInputX > 0))
changeOfDirection = true;
if ((input.InputY > 0 && prevInputY < 0) || (input.InputY < 0 && prevInputY > 0))
changeOfDirection = true;
if(changeOfDirection)
vel.Linear = float3.zero;
vel.Angular = float3.zero;
if(Vector3.SqrMagnitude(new Vector3(vel.Angular.x + vel.Linear.x, vel.Angular.y + vel.Linear.y, vel.Angular.z + vel.Linear.z )) < maxPlayerSpeed * maxPlayerSpeed)
vel.Linear += new float3(input.InputX * deltaTime * moveSpeed, 0f, input.InputY * deltaTime * moveSpeed);
prevInputX = input.InputX;
prevInputY = input.InputY;
}
else
{
if (isJumping) return;
vel.Linear = math.lerp(vel.Linear, float3.zero, deltaTime * 20f);
vel.Angular = math.lerp(vel.Angular, float3.zero, deltaTime * 20f);
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if(PlayerManager.instance == null)
throw new System.NotImplementedException();
var playerMovementJob = new PlayerMovementJob
{
deltaTime = Time.deltaTime,
jumpPressed = Input.GetButtonDown("Jump"),
moveSpeed = PlayerManager.instance.moveSpeed,
jumpForce = PlayerManager.instance.jumpForce,
maxPlayerSpeed = PlayerManager.instance.maxPlayerSpeed,
groundCheckDistance = PlayerManager.instance.groundCheckDistance
};
return playerMovementJob.Schedule(this, inputDeps);
}
}
Help!!
Answer by gameDevMode · Nov 27, 2019 at 05:53 PM
Hi There! I Solved the issue by implementing the following code:
[BurstCompile]
private struct Move : IJobForEach<PlayerMove, Translation, PhysicsVelocity, PhysicsMass>
{
public float3 Target;
public void Execute([ReadOnly] ref PlayerMove player, [ReadOnly] ref Translation translation, ref PhysicsVelocity velocity, ref PhysicsMass mass)
{
var v = math.normalize(this.Target - translation.Value).xz * player.Speed; // 10
var l = velocity.Linear;
l.x = v.x;
l.z = v.y;
velocity.Linear = l;
mass.InverseInertia = float3.zero; // temp fix until freeze rotation added to inspector.
}
}
Hey gameDev$$anonymous$$ode do you $$anonymous$$d explaining your answer?
I have a similar issue with PhysicsVelocity where adjusting it causes "Jitter"
I am not sure exactly what in your code fixed it. Was it the math.normalize?
I think it had to do something with setting the linear velocity to vector v, been a long time man, sorry can't do much.