Add CharacterController.Resize for quick crouching implementation for characters
This commit is contained in:
@@ -96,15 +96,11 @@ void CharacterController::SetStepOffset(float value)
|
||||
{
|
||||
if (value == _stepOffset)
|
||||
return;
|
||||
|
||||
_stepOffset = value;
|
||||
|
||||
if (_controller)
|
||||
{
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE);
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE));
|
||||
}
|
||||
}
|
||||
@@ -180,6 +176,37 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
|
||||
return result;
|
||||
}
|
||||
|
||||
void CharacterController::Resize(float height, float radius)
|
||||
{
|
||||
const float heightDiff = height - _height;
|
||||
const float radiusDiff = radius - _radius;
|
||||
if (Math::IsZero(heightDiff) && Math::IsZero(radiusDiff))
|
||||
return;
|
||||
_height = height;
|
||||
_radius = radius;
|
||||
if (_controller)
|
||||
{
|
||||
float centerDiff = heightDiff * 0.5f + radiusDiff;
|
||||
|
||||
// Change physics size
|
||||
GetControllerSize(height, radius);
|
||||
PhysicsBackend::SetControllerSize(_controller, radius, height);
|
||||
Vector3 positionDelta = _upDirection * centerDiff;
|
||||
|
||||
// Change physics position to maintain feet placement (base)
|
||||
Vector3 position = PhysicsBackend::GetControllerPosition(_controller);
|
||||
position += positionDelta;
|
||||
_center += positionDelta;
|
||||
PhysicsBackend::SetControllerPosition(_controller, position);
|
||||
|
||||
// Change actor position
|
||||
_isUpdatingTransform = true;
|
||||
SetPosition(position - _center);
|
||||
_isUpdatingTransform = false;
|
||||
}
|
||||
UpdateBounds();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
@@ -187,23 +214,45 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
|
||||
|
||||
void CharacterController::DrawPhysicsDebug(RenderView& view)
|
||||
{
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||
Quaternion rotation = Quaternion::Euler(90, 0, 0);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
if (view.Mode == ViewMode::PhysicsColliders)
|
||||
DEBUG_DRAW_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
|
||||
DEBUG_DRAW_CAPSULE(position, rotation, _radius, _height, Color::LightYellow, 0, true);
|
||||
else
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow * 0.8f, 0, true);
|
||||
}
|
||||
|
||||
void CharacterController::OnDebugDrawSelected()
|
||||
{
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||
Quaternion rotation = Quaternion::Euler(90, 0, 0);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false);
|
||||
if (_contactOffset > 0)
|
||||
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius - _contactOffset, _height, Color::Blue.AlphaMultiplied(0.4f), 0, false);
|
||||
#if 0
|
||||
// More technical visuals debugging
|
||||
if (_controller)
|
||||
{
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerBasePosition(_controller), 5.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller), 4.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(PhysicsBackend::GetControllerPosition(_controller) - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(PhysicsBackend::GetControllerPosition(_controller), Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(position, 3.0f), Color::GreenYellow, 0, false);
|
||||
#else
|
||||
if (_controller)
|
||||
{
|
||||
// Physics backend capsule shape
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
DEBUG_DRAW_WIRE_CAPSULE(PhysicsBackend::GetControllerPosition(_controller), rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Base
|
||||
Collider::OnDebugDrawSelected();
|
||||
@@ -216,8 +265,10 @@ void CharacterController::CreateController()
|
||||
// Create controller
|
||||
ASSERT(_controller == nullptr && _shape == nullptr);
|
||||
_cachedScale = GetScale().GetAbsolute().MaxValue();
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
|
||||
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, radius, height, _stepOffset, _shape);
|
||||
|
||||
// Setup
|
||||
PhysicsBackend::SetControllerUpDirection(_controller, _upDirection);
|
||||
@@ -240,13 +291,24 @@ void CharacterController::UpdateSize() const
|
||||
{
|
||||
if (_controller)
|
||||
{
|
||||
const float scaling = _cachedScale.GetAbsolute().MaxValue();
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
PhysicsBackend::SetControllerSize(_controller, radius, height);
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterController::GetControllerSize(float& height, float& radius) const
|
||||
{
|
||||
height = Math::Abs(_height) * _cachedScale;
|
||||
radius = Math::Abs(_radius) * _cachedScale;
|
||||
{
|
||||
// Exclude contact offset around the capsule (otherwise character floats in the air)
|
||||
radius = radius - Math::Max(_contactOffset, 0.0f);
|
||||
}
|
||||
height = Math::Max(height, CC_MIN_SIZE);
|
||||
radius = Math::Max(radius, CC_MIN_SIZE);
|
||||
}
|
||||
|
||||
void CharacterController::CreateShape()
|
||||
{
|
||||
// Not used
|
||||
@@ -254,9 +316,9 @@ void CharacterController::CreateShape()
|
||||
|
||||
void CharacterController::UpdateBounds()
|
||||
{
|
||||
const float scaling = GetScale().GetAbsolute().MaxValue();
|
||||
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
|
||||
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
|
||||
_cachedScale = GetScale().GetAbsolute().MaxValue();
|
||||
float height, radius;
|
||||
GetControllerSize(height, radius);
|
||||
const Vector3 position = _transform.LocalToWorld(_center);
|
||||
const Vector3 extent(radius, height * 0.5f + radius, radius);
|
||||
_box = BoundingBox(position - extent, position + extent);
|
||||
|
||||
@@ -84,13 +84,13 @@ public:
|
||||
API_PROPERTY() void SetRadius(float value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
|
||||
/// Gets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
|
||||
/// </summary>
|
||||
API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
|
||||
float GetHeight() const;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
|
||||
/// Sets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetHeight(float value);
|
||||
|
||||
@@ -194,6 +194,13 @@ public:
|
||||
/// <returns>The collision flags. It can be used to trigger various character animations.</returns>
|
||||
API_FUNCTION() CollisionFlags Move(const Vector3& displacement);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the character height and center position to ensure its feet position stays the same. This can be used to implement a 'crouch' functionality for example. Maintains the same actor position to stay in the middle of capsule by adjusting center of collider accordingly to height difference.
|
||||
/// </summary>
|
||||
/// <param name="height">The height of the capsule, measured in the object's local space.</param>
|
||||
/// <param name="radius">The radius of the capsule, measured in the object's local space.</param>
|
||||
API_FUNCTION() void Resize(float height, float radius);
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Creates the physics actor.
|
||||
@@ -210,6 +217,9 @@ protected:
|
||||
/// </summary>
|
||||
void UpdateSize() const;
|
||||
|
||||
private:
|
||||
void GetControllerSize(float& height, float& radius) const;
|
||||
|
||||
public:
|
||||
// [Collider]
|
||||
#if USE_EDITOR
|
||||
@@ -220,6 +230,7 @@ public:
|
||||
void AddMovement(const Vector3& translation, const Quaternion& rotation) override;
|
||||
bool CanAttach(RigidBody* rigidBody) const override;
|
||||
RigidBody* GetAttachedRigidBody() const override;
|
||||
void SetCenter(const Vector3& value) override;
|
||||
|
||||
// [IPhysicsActor]
|
||||
void OnActiveTransformChanged() override;
|
||||
|
||||
@@ -3157,7 +3157,7 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic
|
||||
desc.material = DefaultMaterial;
|
||||
const float minSize = 0.001f;
|
||||
desc.height = Math::Max(height, minSize);
|
||||
desc.radius = Math::Max(radius - Math::Max(contactOffset, 0.0f), minSize);
|
||||
desc.radius = Math::Max(radius, minSize);
|
||||
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize);
|
||||
auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc);
|
||||
PxRigidActor* actorPhysX = controllerPhysX->getActor();
|
||||
@@ -3183,7 +3183,7 @@ void PhysicsBackend::SetControllerSize(void* controller, float radius, float hei
|
||||
{
|
||||
auto controllerPhysX = (PxCapsuleController*)controller;
|
||||
controllerPhysX->setRadius(radius);
|
||||
controllerPhysX->resize(height);
|
||||
controllerPhysX->setHeight(height);
|
||||
}
|
||||
|
||||
void PhysicsBackend::SetControllerSlopeLimit(void* controller, float value)
|
||||
@@ -3204,6 +3204,12 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value)
|
||||
controllerPhysX->setStepOffset(value);
|
||||
}
|
||||
|
||||
Vector3 PhysicsBackend::GetControllerBasePosition(void* controller)
|
||||
{
|
||||
auto controllerPhysX = (PxCapsuleController*)controller;
|
||||
return P2C(controllerPhysX->getFootPosition());
|
||||
}
|
||||
|
||||
Vector3 PhysicsBackend::GetControllerUpDirection(void* controller)
|
||||
{
|
||||
auto controllerPhysX = (PxCapsuleController*)controller;
|
||||
|
||||
@@ -248,6 +248,7 @@ public:
|
||||
static void SetControllerSlopeLimit(void* controller, float value);
|
||||
static void SetControllerNonWalkableMode(void* controller, int32 value);
|
||||
static void SetControllerStepOffset(void* controller, float value);
|
||||
static Vector3 GetControllerBasePosition(void* controller);
|
||||
static Vector3 GetControllerUpDirection(void* controller);
|
||||
static void SetControllerUpDirection(void* controller, const Vector3& value);
|
||||
static Vector3 GetControllerPosition(void* controller);
|
||||
|
||||
Reference in New Issue
Block a user