I think I see the light in the end of the tunnel.
It seems that the problem of drag-force which parallel to the wall appears because of the two green and two yellow lines. Green line is raycast to detect collider, the yellow one is adjusted movement to prevent player fly inside the wall.
using UnityEngine;
using UnityEngine.EventSystems;
// Program to control simple camera with collider on desired height, with help of Mr. Gemini (AI Google)
public class CombinedCameraController : MonoBehaviour
{
// FIELDS
[SerializeField]
float _mouseSensitivity = 2.0f;
[SerializeField]
float _rotationSpeed = 100.0f;
[SerializeField]
float _smoothTime = 0.5f;
[SerializeField]
float _moveSpeed = 3.0f;
[SerializeField]
float _raycastOffset = 0.2f;
[SerializeField]
float _moveDistanceOffset = 0.3f;
[SerializeField]
float _clampRotationDegree = 80f;
[SerializeField]
float _minMoveMagnitude = 0.001f;
[SerializeField]
float _cameraRotationX = 0f;
[SerializeField]
float _playerRotationY = 0f;
[SerializeField]
float _triggerColliderDetectionRadius = 0.5f;
[SerializeField]
float _collisionCheckDistance = 0.1f;
[SerializeField]
float _pushBackPreventionDistance = 0.01f;
[SerializeField]
float _debugRayLength = 1.0f;
RaycastHit _debugHit;
Vector3 _debugAdjustedMoveDirection;
Vector3 _debugMoveDirection;
Vector3 _debugRaycastOrigin;
bool _isMoving;
bool _drawDebugRays;
Vector3 _lastPosition;
Rigidbody _rigidbody;
Transform _playerTransform;
Quaternion _cameraInitialLocalRotation;
// METHODS
// This method calls before the first frame
void Start()
{
_playerTransform = transform.parent;
_rigidbody = GetComponentInParent<Rigidbody>();
if (_playerTransform == null)
{
Debug.LogError("Parent Transform not found!");
enabled = false;
return;
}
if (_rigidbody == null)
{
Debug.Log("Initialization ERROR! Parent Rigidbody component cannot be found.");
enabled = false;
return;
}
_cameraInitialLocalRotation = transform.localRotation;
_rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
//_rigidbody.isKinematic = true;
_rigidbody.useGravity = false;
_rigidbody.freezeRotation = true;
_lastPosition = _rigidbody.position;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
// This method calls per frame
void FixedUpdate()
{
HandlePlayerMovement();
}
void Update()
{
HandlePlayerRotation();
HandleCameraRotation();
//OnDrawGizmosSelected();
#if UNITY_EDITOR
if (_isMoving)
{
if (_drawDebugRays)
{
Debug.DrawRay(_debugRaycastOrigin, _debugMoveDirection * 2f, Color.red, Time.deltaTime);
Debug.DrawRay(_debugRaycastOrigin, _debugMoveDirection.normalized * _debugHit.distance, Color.green, Time.deltaTime);
Debug.DrawRay(_rigidbody.position, _debugAdjustedMoveDirection, Color.yellow, Time.deltaTime);
Debug.Log($"HIT DISTANCE: {_debugHit.distance}\nADJUSTED MOVE: {_debugAdjustedMoveDirection}");
}
else
{
Debug.DrawRay(_debugRaycastOrigin, _debugMoveDirection * 2f, Color.blue, Time.deltaTime);
}
}
#endif
}
// Only left and right
public void HandlePlayerRotation()
{
// Player Rotation (Y-axis Rotation - A/D keys)
float horizontalInput = Input.GetAxis("Horizontal");
_playerRotationY += horizontalInput * _rotationSpeed * Time.deltaTime;
// Smooth player rotation using Quaternion.Slerp (Spherical-linear interpolation)
Quaternion targetPlayerRotation = Quaternion.Euler(0f, _playerRotationY, 0f);
_playerTransform.localRotation = Quaternion.Slerp(_playerTransform.localRotation, targetPlayerRotation, _smoothTime);
}
// Only back and forward
public void HandlePlayerMovement()
{
float movePerFrame = _moveSpeed * Time.fixedDeltaTime;
float verticalInput = Input.GetAxis("Vertical");
Vector3 moveDirection = _playerTransform.forward * movePerFrame * verticalInput;
_isMoving = moveDirection.magnitude > _minMoveMagnitude;
if (_isMoving)
{
Vector3 adjustedDirection;
if (CheckCollision(moveDirection, out adjustedDirection))
{
_rigidbody.MovePosition(_rigidbody.position + adjustedDirection);
}
else
{
_rigidbody.MovePosition(_rigidbody.position + moveDirection);
}
}
else
{
_drawDebugRays = false;
}
}
// Only up and down
public void HandleCameraRotation()
{
// Mouse Look (Camera X-axis Rotation)
float mouseY = Input.GetAxis("Mouse Y") * _mouseSensitivity;
_cameraRotationX += mouseY;
_cameraRotationX = Mathf.Clamp(_cameraRotationX, -_clampRotationDegree, _clampRotationDegree);
// Smooth camera rotation using Quaternion.Slerp (THIS IS THE KEY by Mr. Gemini)
Quaternion targetCameraRotation = _cameraInitialLocalRotation * Quaternion.Euler(_cameraRotationX, 0f, 0f);
transform.localRotation = Quaternion.Slerp(transform.localRotation, targetCameraRotation, _smoothTime);
}
public bool CheckCollision(Vector3 moveDirection, out Vector3 adjustedMoveDirection)
{
adjustedMoveDirection = moveDirection;
Vector3 raycastOrigin = _playerTransform.position + moveDirection.normalized * _collisionCheckDistance;
_debugMoveDirection = moveDirection;
_debugRaycastOrigin = raycastOrigin;
float raycastDistance = moveDirection.magnitude + _collisionCheckDistance;
bool isCollider = Physics.Raycast(raycastOrigin, moveDirection, out RaycastHit hit, moveDirection.magnitude + _raycastOffset);
if ((hit.collider != null) && !hit.collider.isTrigger) // Only adjust movement if hit non-trigger collider
{
float moveDistance = hit.distance - _collisionCheckDistance - _pushBackPreventionDistance;
if (moveDistance > 0f)
{
adjustedMoveDirection = moveDirection.normalized * moveDistance;
// Prvent vertical motion
adjustedMoveDirection.y = 0;
_debugHit = hit;
_debugAdjustedMoveDirection = adjustedMoveDirection;
_drawDebugRays = true;
return true;
}
else
{
_drawDebugRays = false;
}
}
return false;
}
public Collider[] HandleTriggerDetection()
{
Collider[] hitColliders = Physics.OverlapSphere(transform.position, _triggerColliderDetectionRadius);
foreach (Collider collider in hitColliders)
{
if (collider.isTrigger)
{
Debug.Log("Trigger entered: " + collider.name);
}
}
return hitColliders;
}
public void PreventPushback()
{
_rigidbody.MovePosition(_lastPosition);
}
//private void OnDrawGizmosSelected()
//{
// if (_isColliding)
// {
// Gizmos.color = Color.red;
// Gizmos.DrawLine(_debugRaycastOrigin, _debugMoveDirection);
// Gizmos.color = Color.green;
// Gizmos.DrawLine(_debugRaycastOrigin, _debugMoveDirection.normalized * _debugHit.distance);
// Gizmos.color = Color.yellow;
// Gizmos.DrawLine(_rigidbody.position, _debugAdjustedMoveDirection);
// }
//}
}
Maybe somebody will see it useful — the script I created with help of AI Gemini to follow the player object during debugging MS Visual Studio in the scene view. It should be placed into Assets —> Editor
using UnityEngine;
using UnityEditor;
[InitializeOnLoad]
public class SceneViewFollow : MonoBehaviour
{
[SerializeField]
static Transform _target;
[SerializeField]
static Vector3 _offset = new Vector3(0, 2, 0);
static SceneViewFollow()
{
SceneView.duringSceneGui += OnSceneGui;
}
static void OnSceneGui(SceneView sceneView)
{
if ((Selection.activeTransform != null) && Selection.activeTransform.CompareTag("Player"))
{
_target = Selection.activeTransform;
}
else
{
_target = null;
}
if (_target != null)
{
sceneView.FrameSelected();
//Quaternion targetRotation = _target.rotation;
//Vector3 targetPosition = _target.position + (targetRotation * _offset);
//sceneView.LookAt(targetPosition, targetRotation, 5);
}
}
}
How to fix ‘drag force’ if angle different from 90 degrees in collision?
Also I need to organize the code OMG… I mean need one class for debug stuff, of few… etc cetra, can somebody make some advice? To this process not take another few days…