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)"
Related
I followed https://www.youtube.com/watch?v=zPFU30tbyKs&t=1676s tutorial on ML agents. I didn't change almost anything from the code except for the OnEpisodeBegin function where I replaced Vector3.zero; with the starting position of my agent.
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
public class MoveAgent : Agent
{
[SerializeField]
private Transform targetTransform;
public override void OnEpisodeBegin(){
transform.position = new Vector3(42.59956f, 18.9153f, 144.7489f);
}
public override void CollectObservations(VectorSensor sensor){
sensor.AddObservation(transform.position);
sensor.AddObservation(targetTransform.position);
}
public override void OnActionReceived(ActionBuffers actions){
float moveX = actions.ContinuousActions[0];
float moveZ = actions.ContinuousActions[1];
float moveSpeed = 2f;
transform.localPosition += new Vector3(moveX, 0, moveZ) * Time.deltaTime * moveSpeed;
}
public override void Heuristic(in ActionBuffers actionsOut){
ActionSegment<float> continuousActions = actionsOut.ContinuousActions;
continuousActions[0] = Input.GetAxisRaw("Horizontal");
continuousActions[1] = Input.GetAxisRaw("Vertical");
}
private void OnTriggerEnter(Collider other){
if (other.TryGetComponent<Goal>(out Goal goal)){
SetReward(+1f);
EndEpisode();
}
if (other.TryGetComponent<Wall>(out Wall wall)){
SetReward(-1f);
EndEpisode();
}
}
}
`
What could the problem be?
I Am trying to add a swinging system to my game but whenever my player does the swinging animation it goes back to the same place how would I fix something like that? here is a video showing what it does: https://streamable.com/7jxgqx
I Use a character controller to move the player so does anyone know how to prevent the player from snapping back to the place it came from
Problem: https://streamable.com/7jxgqx
my codes: 1)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Swinging : MonoBehaviour
{
[SerializeField]Animator m_Animator;
CharacterController cc;
[SerializeField]GameObject jumpArea;
PlayerController playerController;
private void Start()
{
cc = GetComponent<CharacterController>();
playerController = GetComponent<PlayerController>();
playerController.enabled = true;
cc.enabled = true;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Collider"))
{
StartCoroutine(StartSwinging());
}
}
IEnumerator StartSwinging()
{
m_Animator.SetTrigger("swing");
cc.enabled = false;
playerController.enabled = false;
yield return new WaitForSeconds(2.3f);
m_Animator.SetBool("afterSwing", true);
Destroy(jumpArea);
playerController.enabled = true;
cc.enabled = true;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
CharacterController cc;
public Transform groundCheck;
public LayerMask groundLayer;
float wallJumpVelocity;
public Animator m_Animator;
private Vector3 direction;
public float speed = 5f;
public float jumpForce = 8f;
public float gravity = -20f;
public bool canDoubleJump = true;
public bool isGrounded;
void Start()
{
cc = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
direction.x = horizontalInput * speed;
m_Animator.SetFloat("run", Mathf.Abs(horizontalInput)); // Mathf.Abs i igivea rac modulebi anu |-5| = 5
isGrounded = Physics.CheckSphere(groundCheck.position, 0.2f, groundLayer);
m_Animator.SetBool("isGrounded", isGrounded);
Jump();
if (horizontalInput != 0)
{
Quaternion flip = Quaternion.LookRotation(new Vector3(0, 0, horizontalInput));
transform.rotation = flip;
}
cc.Move(direction * Time.deltaTime);
}
void Jump()
{
// es kodi anichebs chvens motamashes axtomis funqicas
if (isGrounded)
{
canDoubleJump = true;
if (Input.GetButtonDown("Jump"))
{
direction.y = jumpForce;
}
}
else
{
if (canDoubleJump && Input.GetButtonDown("Jump"))
{
m_Animator.SetTrigger("doubleJump");
direction.y = jumpForce;
canDoubleJump = false;
}
}
direction.y += gravity * Time.deltaTime;
}
}
I Tried applying root motion too but it did not work i am using animations from mixamo.
Thanks <3
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);
}
}
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
This question already has answers here:
Unity: Null while making new class instance
(2 answers)
Closed 5 years ago.
The first script is attached to a empty GameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SpinableObject
{
public Transform t;
public float rotationSpeed;
public float minSpeed;
public float maxSpeed;
public float speedRate;
public bool slowDown;
}
public class SpinObject : MonoBehaviour
{
public SpinableObject[] objectsToRotate;
private Rotate _rotate;
private int index = 0;
// Use this for initialization
void Start()
{
_rotate = new Rotate>();
}
// Update is called once per frame
void Update()
{
var _objecttorotate = objectsToRotate[index];
_rotate.rotationSpeed = _objecttorotate.rotationSpeed;
_rotate.minSpeed = _objecttorotate.minSpeed;
_rotate.maxSpeed = _objecttorotate.maxSpeed;
_rotate.speedRate = _objecttorotate.speedRate;
_rotate.slowDown = _objecttorotate.slowDown;
}
}
The second script is attached to the GameObject/s i want to feed with information. So this script is attached to each GameObject separate.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate : MonoBehaviour
{
public float rotationSpeed;
public float minSpeed;
public float maxSpeed;
public float speedRate;
public bool slowDown;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
RotateObject();
}
public void RotateObject()
{
if (rotationSpeed > maxSpeed)
slowDown = true;
else if (rotationSpeed < minSpeed)
slowDown = false;
rotationSpeed = (slowDown) ? rotationSpeed - 0.1f : rotationSpeed + 0.1f;
transform.Rotate(Vector3.forward, Time.deltaTime * rotationSpeed);
}
}
Not sure if this is a good way to what i want to do ?
Second problem is that the variable _rotate in the first script is all the time null:
I'm doing:
_rotate = new Rotate>();
But still here it's null:
_rotate.rotationSpeed = _objecttorotate.rotationSpeed;
I don't think you understand how Unity works.
First, _rotate = new Rotate>(); is not valid C# and will throw an error.
Second, in your case Rotate is a MonoBehaviour which is not attached to a GameObject. I think that whatever you tried to accomplish is maybe a step to far. You can' synchronize the Update-call of a deattached Component (of which I don't even know if it gets its Update-method called) with another object at all. In short: Your code seems nonsense to me.
I'd suggest, you move your RotateObject method into the SpinableObject and call it from SpinObject, instead of shoving stuff into _rotate. This should work.
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class SpinableObject
{
public Transform t;
public float rotationSpeed;
public float minSpeed;
public float maxSpeed;
public float speedRate;
public bool slowDown;
public void RotateObject()
{
if (rotationSpeed > maxSpeed)
slowDown = true;
else if (rotationSpeed < minSpeed)
slowDown = false;
rotationSpeed = (slowDown) ? rotationSpeed - 0.1f : rotationSpeed + 0.1f;
t.Rotate(Vector3.forward, Time.deltaTime * rotationSpeed);
}
}
public class SpinObject : MonoBehaviour
{
[SerializeField]
private SpinableObject[] objectsToRotate;
// Update is called once per frame
void Update()
{
foreach(var spinner in objectsToRotate)
spinner.RotateObject();
}
}