I am working to understand SOLID, and want to know how my Unity C# script could be better formatted to align with the principles. I will provide the script and then an explanation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
Vector3 velocity;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
private bool isGrounded;
void Update()
{
GroundCheck();
Movement();
Gravity();
}
private void GroundCheck()
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if(isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
}
private void Movement()
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
}
private void Gravity()
{
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
This is a script that handles movement of a character, but additionally performs ground checks and gravity of the player. I have seen interfaces used when adding new functionality, and new classes as well in the same script, but I thought for the purposes of this code it would be best to separate each functionality into a function and have them all call from update of the PlayerMovement class. I decided to use one class because I was wary of running three classes with three Update methods in this one script for performance reasons. Is there a better way I should approach the organization of this script from a SOLID point of view? My thought process was that if I wanted to extend these functionalities I would be able to do so in each function, and if I needed new functionality I could create more functions.
you can have one separate class that has the GroundCheck(), Movement(), Gravity() methods, and then you can access that class from the PlayerMovement class. if you make your methods static you will be able to access them. And that way you will have your public and private fields in PlayerMovement class and Update() method. So you call the methods in Update() and add the needed arguments in the brackets.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public CharacterController controller;
public float speed = 12f;
public float gravity = -9.81f;
Vector3 velocity;
public Transform groundCheck;
public float groundDistance = 0.4f;
public LayerMask groundMask;
private bool isGrounded;
Movement movement;
void Start()
{
movement = FindObjectOfType<Movement>();
}
void Update()
{
movement.GroundCheck(isGrounded, groundCheck, groundDistance, groundMask, velocity);
movement.Movement(this.gameObject, controller, speed);
movement.Gravity(velocity, gravity, controller);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
static public void GroundCheck(bool isGrounded, Transform groundCheck, float groundDistance, LayerMask groundMask, Vector3 velocity)
{
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
if(isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
}
static public void Movement(GameObject playerMovement, CharacterController controller, float speed)
{
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = playerMovement.transform.right * x + playerMovement.transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
}
static public void Gravity(Vector3 velocity, float gravity, CharacterController controller)
{
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
Related
I'm trying to make move script but it seems impossible to me because I stared at code for like an hour I even rewrote it but same problem. It appears that after I added gravity and groundcheck things, my character cant run or even walk at set speed (he is moving very slow). Can someone please help me with it please cuz' I'm lost
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
//VARIABLES
[SerializeField] private float moveSpeed;
[SerializeField] private float walkSpeed;
[SerializeField] private float runSpeed;
private Vector3 moveDirection;
private Vector3 velocity;
[SerializeField] private bool isGrounded;
[SerializeField] private float groundCheckDistance;
[SerializeField] private LayerMask groundMask;
[SerializeField] private float gravity;
//REFERENCES
private CharacterController controller;
private void Start()
{
controller = GetComponent<CharacterController>();
}
private void Update()
{
Move();
}
private void Move()
{
isGrounded = Physics.CheckSphere(transform.position, groundCheckDistance, groundMask);
if(isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
float moveZ = Input.GetAxis("Vertical");
moveDirection = new Vector3(0, 0, moveZ);
if(isGrounded)
{
if(moveDirection != Vector3.zero && !Input.GetKey(KeyCode.LeftShift))
{
Walk();
}
else if(moveDirection != Vector3.zero && Input.GetKey(KeyCode.LeftShift))
{
Run();
}
else if(moveDirection == Vector3.zero)
{
Idle();
}
moveDirection *= moveSpeed;
}
controller.Move(moveDirection * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
private void Idle()
{
}
private void Walk()
{
moveSpeed = walkSpeed;
}
private void Run()
{
moveSpeed = runSpeed;
}
}
I found the problem. Problem was with spawn area or call it how you want. If you'll move further the problem is no longer there.
Look at this video here: https://youtu.be/xfxgwmuPQsA
I am really new to unity and i'm trying to make a PlayerController with different states in different files.
this is the code so far:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private PlayerState currentState;
void Start()
{
currentState = new PlayerIdle(this);
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
currentState.OnStateUpdate();
}
public void ChangeState(PlayerState newState)
{
currentState.OnStateExit();
currentState = newState;
newState.OnStateEnter();
}
}
how do i fix this?
edit: here is the PlayerIdle code which is supposed to give me the error.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerIdle : PlayerState
{
public CharacterController controller;
public Transform cam;
public GameObject player = GameObject.Find("Player");
public float speed = 12f;
public float gravity = -9.81f;
public float jumpHeight = 3;
public float rotationSpeed;
float x;
float z;
public Transform groundCheck = GameObject.Find("groundCheck").transform;
public float groundDistance = 0.4f;
public LayerMask groundMask;
[SerializeField] Vector3 velocity;
public bool isGrounded;
public float turnSmoothTime = 1f;
float turnSmoothVelocity;
public PlayerIdle(PlayerController playerController) : base(playerController)
{
this.playerController = playerController;
}
public override void OnStateEnter()
{
}
public override void OnStateExit()
{
}
public override void OnStateUpdate()
{
x = Input.GetAxisRaw("Horizontal");
z = Input.GetAxis("Vertical");
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
velocity = player.transform.TransformDirection(Vector3.forward) * speed * z + new Vector3(0f, velocity.y, 0f); ;
if (Input.GetButtonDown("Jump") && isGrounded)
{
velocity.y = Mathf.Sqrt(jumpHeight * -2 * gravity);
}
if (isGrounded && velocity.y < 0)
{
velocity.y = -2f;
}
velocity.y += gravity * Time.deltaTime;
player.transform.Rotate(new Vector3(0f, x * rotationSpeed, 0f));
controller.Move(velocity * Time.deltaTime);
}
}
any help would be much appreciated :)
also, i found out this is called a statemachine if that helps.
The error says that something in that line is null.
The only thing that can be null is the CurrentState variable.
Probably when you assign it the value of new PlayerIdle (this) in the Start () method, that value is null.
If you can figure out why it's null, you can fix it, but if you're still having trouble you can include the PlayerIdle () code.
A suggestion may be to insert a print (currentState) at the end of Start () to understand if it is actually null.
In addition, also insert a print (new PlayerIdel (this)) in the Update () method to understand if even during the update it continues to be null.
it seems that the update method was being called before the start method (which i still don't understand), so the solution was just to add an if statement before currentState.OnStateUpdate(); saying "if (currentState != null)"
Im trying to make a 3d game with unity, but i dont know how to implement gravity, so i turned gravity for the rigidbody on. Also how could i implement jumping and wallrunning into this code?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement : MonoBehaviour
{
float playerHeight = 2f;
public float moveSpeed = 6f;
float horizontalMovement;
float verticalMovement;
Vector3 moveDirection;
Rigidbody rb;
private void Start()
{
rb = GetComponent <Rigidbody>();
rb.freezeRotation = true;
}
bool isGrounded;
private void Update()
{
isGrounded = Physics.Raycast(transform.position, Vector3.down, playerHeight / 2 + 0.01f);
print (isGrounded);
MyInput();
}
void MyInput()
{
horizontalMovement = Input.GetAxisRaw("Horizontal");
verticalMovement = Input.GetAxisRaw("Vertical");
moveDirection = transform.forward * verticalMovement + transform.right * horizontalMovement;
}
private void FixedUpdate()
{
MovePlayer();
}
void MovePlayer()
{
rb.AddForce(moveDirection.normalized * moveSpeed, ForceMode.Acceleration);
}
}
I have been trying to create a video game but I stuck in this tutorial, in the tutorial it shows us to use input actions addon (What that does is it makes W-A-S-D or joystick type of keys easy to use) but even I watched the video after 3rd time I couldn't find the mistake! the problem is that I am not able to move my player forward or backward, I can only move it right and left. Please help I have tried so many things, like restarting the program or changing some code but it's not working can you help me to fix it?
Here is the first code to take the WASD commands from the addon script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InputManager : MonoBehaviour
{
//Takes the wasd controls from addon
InputWASD playerControls;
public Vector2 movementInput;
public float verticalInput;
public float horizontalInput;
private void OnEnable()
{
if (playerControls == null)
{
playerControls = new InputWASD();
playerControls.PlayerMovement.Movement.performed += i => movementInput = i.ReadValue<Vector2>();
}
playerControls.Enable();
}
private void OnDisable()
{
playerControls.Disable();
}
public void HandleAllInputs()
{
HandleMovementInput();
//TODO: HandleJumpInput
// HandleAttackInput
// HandleDashInput
// HandleAbilityInput
}
private void HandleMovementInput()
{
verticalInput = movementInput.y;
horizontalInput = movementInput.x;
}
}
Here is the second code to use keys in movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAct : MonoBehaviour
{
InputManager inputManager;
Vector3 moveDirection;
Transform cameraObject;
Rigidbody playersRB;
public float moveSpeed = 6f;
public float rotationSpeed = 15f;
private void Awake()
{
inputManager = GetComponent<InputManager>();
playersRB = GetComponent<Rigidbody>();
cameraObject = Camera.main.transform;
}
public void HandleAllAction()
{
HandleMovement();
HandleRotation();
}
private void HandleMovement()
{
moveDirection = cameraObject.forward * inputManager.verticalInput;
moveDirection = moveDirection + cameraObject.right * inputManager.horizontalInput;
moveDirection.Normalize();
moveDirection.y = 0;
moveDirection = moveDirection * moveSpeed;
Vector2 movementVelocity = moveDirection;
playersRB.velocity = movementVelocity;
}
private void HandleRotation()
{
Vector3 targetDirection = Vector3.zero;
targetDirection = cameraObject.forward * inputManager.verticalInput;
targetDirection = targetDirection + cameraObject.right * inputManager.horizontalInput;
targetDirection.Normalize();
targetDirection.y = 0;
if (targetDirection == Vector3.zero)
{
targetDirection = transform.forward;
}
Quaternion targetRotation = Quaternion.LookRotation(targetDirection);
Quaternion playerRotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
transform.rotation = playerRotation;
}
}
Here is the third and last code to use first 2 codes:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMain : MonoBehaviour
{
//Calling scripts
InputManager inputManager;
PlayerAct playerAct;
private void Awake()
{
inputManager = GetComponent<InputManager>();
playerAct = GetComponent<PlayerAct>();
}
private void Update()
{
inputManager.HandleAllInputs();
}
private void FixedUpdate()
{
playerAct.HandleAllAction();
}
}
I did what the man told me (in the tutorial) but mine is not working!
sometime maybe the video is so old and unity is keep updating so some code maybe is not work anymore , maybe you should use the getinput system like float x= Input.GetAxis("Horizontal")); and float z = Input.GetAxis("Vertical"); this two is use characterController and let see how it work
I am trying to make a random Vector3, but Unity is giving me this error: UnityException: Range is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call it in Awake or Start instead. Called from MonoBehavior 'particleMover'.
This is my code:
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using UnityEngine;
public class particleMover : MonoBehaviour
{
public float moveSpeed;
public float temperature;
public Rigidbody rb;
public Transform tf;
static private float[] directions;
// Start is called before the first frame
void Start()
{
System.Random rnd = new System.Random();
float[] directions = { rnd.Next(1, 360), rnd.Next(1, 360), rnd.Next(1, 360) };
}
// Update is called once per frame
void Update()
{
Vector3 direction = new Vector3(directions[0], directions[1], directions[2]);
direction = moveSpeed * direction;
rb.MovePosition(rb.position + direction);
}
}
Vector3 direction = Random.insideUnitSphere;
And you used (1, 360) and it seems you are confusing direction with rotation.
Vector3(x, y, z) - x, y, z are position values, not angles.
In addition, you need to use Time.deltaTime
direction = moveSpeed * direction * Time.deltaTime;
More info: https://docs.unity3d.com/ScriptReference/Time-deltaTime.html
Updated answer:
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using UnityEngine;
public class particleMover : MonoBehaviour
{
public float moveSpeed;
public float temperature;
public Rigidbody rb;
public Transform tf;
private Vector3 direction = Vector3.zero;
void Start()
{
direction = Random.insideUnitSphere;
}
void Update()
{
rf.position += direction * moveSpeed * Time.deltaTime;
// If this script is attached to tf object
// transform.position += direction * moveSpeed * Time.deltaTime;
}
}