Unity DOTS 5日目
今日は早々に記事を書いて大晦日を楽しみたいと思います。
今日の内容
Unity Physicsの公式サンプルのジャンプ構造を頑張って読解しようとしたけど、知識不足だった件
という感じで行きます。誰かわかったら教えてください。
環境
Unity 2022.2.1f1
Entities 1.0.0-pre.15
Burst 1.7.4
Unity Physics 1.0.0-pre.15
Unity PhysicsのVelocityくん
ジャンプするにはお馴染みVelocityくんのUnity Physicsバージョンを知らなければなりません。
こいつは、物体に力を与えるやつですね。
ただ、お馴染みのアイツより難しいです。
AddForce
メソッドが用意されていないからです。
私は数学も得意じゃないので、仕組みは一旦置いといて表面だけ語ります。
力を与える方法
PhysicsVelocity.CalculateVelocityToTarget
というメソッドで計算します。
これは、Entityをある座標(TargetTransform)に行かせるためにはどのくらいの力が必要か計算してくれます。
これをそれぞれのEntityのがもつPhysicsVelocityに代入してあげると力を加えれます。
完成度過去最低のクソコード
流石に公開するのをためらうレベルのコードなのでコピペ厳禁で…w
目的としては普通にキャラクターコントローラーなのでジャンプ以外に移動もできます。
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;
using Unity.Transforms;
using UnityEngine;
using UnityEngine.InputSystem;
using static CubeSystem;
using static UnityEngine.EventSystems.EventTrigger;
public partial class PlayerMoveSystem : SystemBase
{
PlayerInputActions _playerInputActionScript;
InputAction _playerMovement;
InputAction _playerJump;
private bool _isTryJumped = false;
protected override void OnCreate()
{
_playerInputActionScript = new();
_playerInputActionScript.Enable();
_playerMovement = _playerInputActionScript.Player.Move;
_playerJump = _playerInputActionScript.Player.Jump;
}
protected override void OnDestroy()
{
_playerInputActionScript.Disable();
_playerInputActionScript.Dispose();
}
protected override void OnUpdate()
{
Vector2 _input = _playerMovement.ReadValue<Vector2>();
var _deltaTime = UnityEngine.Time.deltaTime;
var _tickTime = 1f / _deltaTime;
var _jumpForce = 0.0f;
//ForEachの中ではフィールドは呼び出せないらしいので、ここで呼び出す。
bool _isPushJumpKey = false;
//処理に時間かかるのか、ダブルジャンプが出来てしまっていたので無理やり修正
if (_isTryJumped)
{
_isTryJumped = false;
}
else
{
_isPushJumpKey = _playerJump.ReadValue<float>() > 0;
_isTryJumped = true;
}
Entities
.WithAll<Player>()
.ForEach((ref PhysicsVelocity _velocity, ref PhysicsMass _mass, ref LocalTransform _transform, ref Player _player) => {
if (_isPushJumpKey && _player._onGround)
{
_jumpForce = 100.0f;
_player._onGround = false;
}
var _movement = new float3(_input.x, _jumpForce, _input.y);
var _targetTransform = new RigidTransform(_transform.Rotation, (_movement * 2 * _deltaTime) + _transform.Position);
_velocity = PhysicsVelocity.CalculateVelocityToTarget(_mass, _transform.Position, _transform.Rotation, _targetTransform, _tickTime);
if(_velocity.Linear.y != 0)
{
Debug.LogWarning(_velocity.Linear);
}
})
.ScheduleParallel();
Dependency = new OnGroundJob
{
_players = GetComponentLookup<Player>(),
_grounds = GetComponentLookup<Ground>(),
}.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), Dependency);
}
}
public partial struct OnGroundJob : ICollisionEventsJob
{
public ComponentLookup<Player> _players;
public ComponentLookup<Ground> _grounds;
public void Execute(CollisionEvent collisionEvent)
{
var _entityAPlayer = _players.HasComponent(collisionEvent.EntityA);
var _entityAGround = _grounds.HasComponent(collisionEvent.EntityB);
var _entityBPlayer = _players.HasComponent(collisionEvent.EntityA);
var _entityBGround = _grounds.HasComponent(collisionEvent.EntityB);
if (_entityBGround && _entityAPlayer)
{
_players.GetRefRWOptional(collisionEvent.EntityA, false).ValueRW._onGround = true;
}
else if(_entityBPlayer && _entityAGround)
{
_players.GetRefRWOptional(collisionEvent.EntityB, false).ValueRW._onGround = true;
}
}
}
イベント発火に関しては昨日の記事をご覧ください。
このコードの問題点
そもそも、このコード見た目やスマートさ以前の問題があります。
ジャンプすると、瞬間移動したように見えるんですよね。
原因としてはPhysicsVelocity.CalculateVelocityToTarget
は一瞬でそこに飛ばすにはどのくらいの力を与えなければならないのかという計算をするからですね。
公式サンプルを読む
公式サンプルを読んでいくと問題の解決策が分かりそうになりました。
公式サンプルは「AnimationCurve」というクラスを使ってるんです。
調べるとこれは、Animationだけに使うクラスではなく徐々に速度が変化していく動作全てに利用できるそうです。
まぁ、そもそもAnimatinCurveの使い方が分からなかったので、終わりです。
終わり
ということで、5日連続で更新してきましたが、DOTSじゃないところで詰んだのでDOTSの内容を書いていくのは終わりかもしれません。
「Havok Physics for Unity」でもジャンプやってみて上手くいきそうだったら更新するかも?
まぁ期待しないでください。
雑記にお付き合いいただきありがとうございました。紅白のSixTONESが楽しみです。バイバイ