I am a student in Japan creating a game using Unity.
Since I don’t understand English very well, I am using a translation app, so my expressions may not be perfect. Please forgive me.
Let me get to the point.
As shown in the GIF, I am experiencing uncontrollable trembling.
However, there seems to be a specific pattern to it, so there must be a cause.
I believe it can be fixed.
However, I don’t know how to fix it, and I don’t know the cause.
Please help me.
Patterns I have identified:
- The trembling starts when I look up or down.
- The more I look, the stronger the trembling gets.
- I am using the same script in another scene, but it does not cause trembling there.
Please help me.

Hello,
Can you upload the script here so I can check it and get back to you on the potential issue.
Thank you for reading my message.
I couldn’t attach the file, so I will include the script directly here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FirstPersonController : MonoBehaviour
{
private Rigidbody rb;
#region Camera Movement Variables
public Camera playerCamera;
public float fov = 60f;
public bool invertCamera = false;
public bool cameraCanMove = true;
public float mouseSensitivity = 2f;
public float maxLookAngle = 45f;
public bool lockCursor = true;
public bool crosshair = true;
public Sprite crosshairImage;
public Color crosshairColor = Color.white;
private float yaw = 0.0f;
private float pitch = 0.0f;
private Image crosshairObject;
#endregion
#region Movement Variables
public bool playerCanMove = true;
public float walkSpeed = 5f;
public float maxVelocityChange = 10f;
private bool isWalking = false;
private bool isGrounded = false;
#endregion
#region Footstep Sound Variables
[SerializeField]
private AudioClip[] footstepSounds;
private AudioSource audioSource;
public float footstepInterval = 0.5f;
private float footstepTimer = 0f;
private bool isLeftFoot = true;
#endregion
#region Head Bob
public bool enableHeadBob = false;
public Transform joint;
public float bobSpeed = 5f;
public Vector3 bobAmount = new Vector3(0.02f, 0.00f, 0f);
private Vector3 jointOriginalPos;
private float timer = 0;
#endregion
#region Flashlight Variables
public Light flashlight;
public KeyCode toggleFlashlightKey = KeyCode.Mouse1;
public AudioClip flashlightOnSound;
public AudioClip flashlightOffSound;
#endregion
#region Interaction Variables
public float interactionDistance = 3f;
#endregion
private void Awake()
{
rb = GetComponent<Rigidbody>();
audioSource = gameObject.AddComponent<AudioSource>();
crosshairObject = GetComponentInChildren<Image>();
playerCamera.fieldOfView = fov;
jointOriginalPos = joint.localPosition;
rb.interpolation = RigidbodyInterpolation.Interpolate;
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
}
private void Start()
{
if (lockCursor)
{
Cursor.lockState = CursorLockMode.Locked;
}
if (crosshair)
{
crosshairObject.sprite = crosshairImage;
crosshairObject.color = crosshairColor;
}
else
{
crosshairObject.gameObject.SetActive(false);
}
if (flashlight == null)
{
GameObject lightGameObject = new GameObject("Flashlight");
lightGameObject.transform.parent = playerCamera.transform;
lightGameObject.transform.localPosition = new Vector3(0, 0, 0.5f);
flashlight = lightGameObject.AddComponent<Light>();
flashlight.type = LightType.Spot;
flashlight.spotAngle = 60f;
flashlight.range = 20f;
flashlight.intensity = 3f;
flashlight.shadows = LightShadows.Soft;
flashlight.enabled = false;
}
RenderSettings.ambientIntensity = 0.2f;
}
private void Update()
{
HandleCameraRotation();
HandleFlashlight();
HandleFootsteps();
HandleDoorInteraction();
if (enableHeadBob && isWalking)
{
// 視点移動中はHead Bobのタイマーをリセット
if (Mathf.Abs(Input.GetAxis("Mouse Y")) > 0.1f)
{
timer = 0; // タイマーをリセットして上下視点移動時に揺れが始まらないように
}
else
{
ApplyHeadBob(); // 通常通りHead Bobを適用
}
}
}
private void HandleCameraRotation()
{
if (cameraCanMove)
{
// 水平方向の回転
yaw += Input.GetAxis("Mouse X") * mouseSensitivity;
// 垂直方向の回転
float mouseYInput = Input.GetAxis("Mouse Y") * mouseSensitivity;
if (!invertCamera)
pitch -= mouseYInput;
else
pitch += mouseYInput;
// 垂直回転角度をクランプして揺れを防ぐ
pitch = Mathf.Clamp(pitch, -maxLookAngle, maxLookAngle);
// プレイヤー本体の回転
transform.localEulerAngles = new Vector3(0, yaw, 0);
// カメラの回転を滑らかに反映させる
playerCamera.transform.localEulerAngles = new Vector3(pitch, 0, 0);
}
}
private void HandleFlashlight()
{
if (Input.GetKeyDown(toggleFlashlightKey))
{
flashlight.enabled = !flashlight.enabled;
if (flashlight.enabled)
{
if (flashlightOnSound != null)
audioSource.PlayOneShot(flashlightOnSound);
RenderSettings.ambientIntensity = 0.8f;
}
else
{
if (flashlightOffSound != null)
audioSource.PlayOneShot(flashlightOffSound);
RenderSettings.ambientIntensity = 0.2f;
}
}
if (flashlight != null)
flashlight.transform.rotation = playerCamera.transform.rotation;
}
private void HandleFootsteps()
{
isWalking = (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0);
if (isWalking)
{
footstepTimer -= Time.deltaTime;
if (footstepTimer <= 0f)
{
PlayFootstepSound();
footstepTimer = footstepInterval;
}
}
}
private void HandleDoorInteraction()
{
RaycastHit hit;
if (Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, interactionDistance))
{
DoorScript.Door door = hit.transform.GetComponent<DoorScript.Door>();
if (door != null)
{
if (Input.GetKeyDown(KeyCode.E))
{
door.ToggleDoor();
}
}
}
}
private void PlayFootstepSound()
{
int randomIndex = Random.Range(0, footstepSounds.Length);
audioSource.PlayOneShot(footstepSounds[randomIndex]);
isLeftFoot = !isLeftFoot;
}
private void ApplyHeadBob()
{
timer += Time.deltaTime * bobSpeed;
joint.localPosition = jointOriginalPos + new Vector3(0, Mathf.Sin(timer) * bobAmount.y, 0);
}
private void FixedUpdate()
{
if (playerCanMove)
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
movement = transform.TransformDirection(movement);
Vector3 targetVelocity = movement * walkSpeed;
Vector3 velocity = rb.linearVelocity;
Vector3 velocityChange = targetVelocity - velocity;
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
rb.AddForce(velocityChange, ForceMode.VelocityChange);
}
}
}
So, based on your script (and I can test when I am at home, currently away from my work pc as im at work…its complicated) You are resetting the HeadBob if your absolute value of Input Axis Y is greater than 0.1f, so any time you look up, it will push it back down resetting the head bob. Remove the absolute (Mathf.Abs(Input.GetAxis(“Mouse Y”)) and instead take a normalized version. Check for greater than and equal to.
off topic, but your flashlight, you can create in editor and just activate and deactivate it with code, no need to create the light in the code.
Be careful with HandleFootsteps, the code although works, would mean you could be flying in the air and have footsteps sound. Try linking the audio clip to animation HIT events for smoother footstep interaction.
I shall copy the code over into a test environment when I have the chance and get back to you soon.
In JAPANESE
したがって、スクリプトに基づいて(そして、私が自宅にいて、現在仕事中のように仕事用PCから離れているときにテストできます。複雑です)入力軸Yの絶対値が0.1fより大きい場合、HeadBobをリセットしています。 , そのため、上を向くたびに、下に押し戻されて頭のボブがリセットされます。絶対値 (Mathf.Abs(Input.GetAxis(“Mouse Y”)) を削除し、代わりに正規化されたバージョンを取得します。以上か等しいかを確認します。
話は逸れますが、懐中電灯はエディターで作成し、コードで有効化または無効化するだけで、コードでライトを作成する必要はありません。
HandleFootsteps には注意してください。コードは機能しますが、空を飛んでいて足音が聞こえる可能性があります。足音のインタラクションをよりスムーズにするために、オーディオ クリップをアニメーション HIT イベントにリンクしてみてください。
機会があればコードをテスト環境にコピーして、すぐにご連絡いたします。
I ran your script on a gameObject, I could not replicate the head bobbing, I used it on a simple sphere Object, perhaps it is something to do with the Joint variable. As I have not got the environment you have my testing is limited. However the script works as intended, although a little messy its good. I can move, rotate camera etc. Your original issue was trembling, that may be a conflict of input. But it works is the first thing.
Thank you for your message.
Since then, I have tried various things and have come to the conclusion that the issue may lie with the house object.
Although the error does not stop the script, it is possible that a negative value in the collider is causing the problem. Currently, I am using a Box Collider, but I am considering the possibility of changing it. However, I am aware that making such a change might be challenging. I am also wondering if a Mesh Collider might be a better option.
Could you provide me with some advice on this? I understand that due to time zone differences, a real-time response might be difficult.
Thank you again for taking the time to respond to me.
I work nights, so do not worry. So different colliders have different issues:
Box collider is simple, but not the most accurate for certain meshes.
Mesh Collider is more complex and more accurate but can slow down the game (due to calculations) it is barely noticeable on one or two objects, but if you have thousands it becomes noticeable.
There is an option where you can make multiple colliders and convert them into a single collider (Composite Collider) where many colliders act as one single collider.
This is a mix of both simple and complex, while taking longer to set up, it uses simple colliders and makes them act more like a mesh collider
Try this: go in Rigidbody > collision detection > continuous dynamic