I'm started with Unity, and I need (before game is in paused) trigger an avent. Is there any way to trigger any event when the Time.timeScale is changed to 0? Something like: Time.timeScale.onBeforeChange()...
Thanks a lot.
Make the thing that changes the time scale a controller, then make that controller raise the event.
[System.Serializable]
public class BeforeTimeChangedData
{
public bool canceled;
public float oldValue;
public float newValue;
}
[System.Serializable]
public class BeforeTimeChangedEvent : UnityEvent<BeforeTimeChangedData>
{
}
//Attach this to a game object that gets loaded in your pre-load scene with the tag "Singletons"
public class TimeController : MonoBehaviour
{
void Awake()
{
DontDestroyOnLoad(gameObject);
if(BeforeTimeChanged == null)
BeforeTimeChanged = new BeforeTimeChangedEvent();
}
public BeforeTimeChangedEvent BeforeTimeChanged;
public bool ChangeTimeScale(float newValue)
{
var args = new BeforeTimeChangedData();
args.oldValue = Time.timeScale;
args.newValue = Time.timeScale;
BeforeTimeChanged.Invoke(args);
if(!args.canceled)
{
Time.timeScale = newValue;
}
return args.canceled;
}
}
Elsewhere you can change the timescale by doing
public class TimeSlower : MonoBehaviour
{
private TimeController _timeController;
public Text TimeChanged;
void Start()
{
var singletons = GameObject.FindWithTag("Singletons");
_timeController = singletons.GetComponent<TimeController>();
if(_timeController == null)
throw new System.ArgumentNullException("Could not find a TimeController on the Singletons object");
}
void Update()
{
if(Input.GetButton("SlowTime"))
{
var changed = _timeController.ChangeTimeScale(0.5f);
if(changed)
{
TimeChanged.text = "Time Changed!";
}
}
}
}
And here is another component that listens for the change and cancels the change if the change has happened too recently;
public class TimeChangeLimiter : MonoBehaviour
{
private float lastTimeChange = 0;
private TimeController _timeController;
public Text TimeChanged;
[Range(0, float.MaxValue)]
public float Cooldown;
void Start()
{
var singletons = GameObject.FindWithTag("Singletons");
_timeController = singletons.GetComponent<TimeController>();
if(_timeController == null)
throw new System.ArgumentNullException("Could not find a TimeController on the Singletons object");
_timeController.BeforeTimeChanged.AddListener(OnBeforeTimeChanged);
}
void OnDestroy()
{
_timeController.BeforeTimeChanged.RemoveListener(OnBeforeTimeChanged);
}
void OnBeforeTimeChanged(BeforeTimeChangedData args)
{
if(Time.time - lastTimeChange < Cooldown)
{
args.canceled = true;
return;
}
lastTimeChange = Time.time;
}
}
Related
So I have Ctr C Ctr V an Interaction System from YT channel.
I have perfectly copied line by line But the only issue was that he was using the old input system and used KeyPressedDown and keyPressedUp to make 2 boolean's which change according what state is the keypressed, I tried to mimic it with new Input system by making it from a button to a Axis value which follows the logic of if its ClickedAxis>0.4 its true and ClockedAxis==0 false, as a quick press it worked so I was happy BUT here comes the bug
My if statement is Being Spammed Constantly like none stop. My statement basically say's that if the Interacting is true Do :
bool interacting;
If(interacting)
{
float HoldingDownTimer+=Time.DeltaTime;
if(HoldingDownTimer>=5)
{
//Do your task;
}
}
but When I run the statement For some reason it prints 0.0110823 values which are stuck at that range of number's no lower no higher, My theory is that its been spam called.
Here's the Script
If your interested for all the components the YT channel is this guy's VeryHotShark
using UnityEngine;
using NaughtyAttributes;
public class LocomotionScript : MonoBehaviour
{
#region Data
[BoxGroup("Animation Handling Data")]
Animator animator;
int isWalkingHash;
[SerializeField] bool movementPressed;
[BoxGroup("Input Handling Data")]
private CharacterController controller;
public MovementInput input;
Vector2 currentMovement;
[SerializeField] float speed = 1;
private Vector3 velocity;
private float gravity = -9.81f;
public Transform ground;
public float distanceToGround = 0.4f;
public LayerMask groundMask;
private Vector2 smoothinMovement;
private float smoothInputSpeed;
private Vector2 tempCurrentMovement;
private bool isGrounded;
public InteractionInputData interactionInput;
#endregion
private void Awake()
{
animator = GetComponent<Animator>();
input = new MovementInput();
controller = GetComponent<CharacterController>();
input.KeyBoard.ASWD.performed += ctx =>
{
currentMovement = ctx.ReadValue<Vector2>();
movementPressed = currentMovement.x != 0 || currentMovement.y != 0;
};
}
private void Start()
{
interactionInput.Reset();
}
void GetInteractionInputData()
{
interactionInput.InteractedClicked = input.KeyBoard.Interact.ReadValue<float>() > 0.1f;
interactionInput.InteractedReleased = input.KeyBoard.Interact.ReadValue<float>() == 0f;
}
private void Update()
{
LocoMotion();
Grav();
Interact();
GetInteractionInputData();
}
void Grav()
{
isGrounded = Physics.CheckSphere(ground.position, distanceToGround, groundMask);
if (isGrounded && velocity.y<0)
{
velocity.y = -2f;
}
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
void Interact()
{
}
void LocoMotion()
{
tempCurrentMovement=Vector2.SmoothDamp(tempCurrentMovement, currentMovement, ref smoothinMovement, smoothInputSpeed);
Vector3 movement = (tempCurrentMovement.y * transform.forward) + (tempCurrentMovement.x * transform.right);
WalkAnimation();
controller.Move(movement * speed * Time.deltaTime);
}
void WalkAnimation()
{
animator.SetBool("WalkingHush", movementPressed);
}
private void OnEnable()
{
input.KeyBoard.ASWD.Enable();
input.KeyBoard.Interact.Enable();
}
private void OnDisable()
{
input.KeyBoard.ASWD.Enable();
input.KeyBoard.Interact.Enable();
}
}
//
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace th
{
public class InteractionController : MonoBehaviour
{
#region Variables
[Header("Data")]
public InteractionData interactionData;
public InteractionInputData interactionInputData;
[Space]
[Header("RaySetting's")]
public float rayDistance;
public float raySphereRadius;
public LayerMask interactableLayer;
#endregion
#region Private
private Camera m_cam;
private bool m_interacting;
private float m_holderTimer = 0.0f;
#endregion
#region BuildIn
private void Awake()
{
m_cam = FindObjectOfType<Camera>();
}
private void Update()
{
CheckForInteractable();
CheckForInteractableInput();
}
#endregion
#region Crafted Methodds
void CheckForInteractable()
{
Ray _ray = new Ray(m_cam.transform.position, m_cam.transform.forward);
RaycastHit _hitInfo;
bool _hitSomething = Physics.SphereCast(_ray, raySphereRadius, out _hitInfo,
rayDistance,interactableLayer);
if (_hitSomething)
{
InteractableBase _interactable = _hitInfo.transform.GetComponent<InteractableBase>();
if (_interactable != null)
{
if (interactionData.isEmpty())
{
interactionData.Interactable = _interactable;
}
else
{
if (!interactionData.IsSameInteractible(_interactable))
interactionData.Interactable = _interactable;
}
}
}
else
{
interactionData.ResetData();
}
Debug.DrawRay(_ray.origin, _ray.direction * rayDistance, _hitSomething ? Color.green : Color.red);
}
void CheckForInteractableInput()
{
if (interactionData.isEmpty())
{
return;
}
if (interactionInputData.InteractedClicked)
{
m_interacting = true;
m_holderTimer = 0f;
}
if (interactionInputData.InteractedReleased)
{
m_interacting = false;
m_holderTimer = 0f;
}
if (m_interacting)
{
if (!interactionData.Interactable.IsInteractible)
return;
if (interactionData.Interactable.HoldInteract)
{
m_holderTimer += Time.deltaTime;
Debug.Log(m_holderTimer);
if (m_holderTimer >= interactionData.Interactable.holdDuration)
{
interactionData.Interact();
m_interacting = false;
}
}
else
{
interactionData.Interact();
m_interacting = false;
}
}
}
#endregion
}
}
///
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace th
{
[CreateAssetMenu(fileName ="Interaction Data",menuName = "InteractionSystem/InteractionData")]
public class InteractionData : ScriptableObject
{
private InteractableBase m_interactible;
public InteractableBase Interactable
{
get => m_interactible;
set => m_interactible = value;
}
public void Interact()
{
m_interactible.OnInteract();
ResetData();
}
public bool IsSameInteractible(InteractableBase _newInteractible) => m_interactible == _newInteractible;
public bool isEmpty() => m_interactible == null;
public void ResetData()=> m_interactible = null;
}
}
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "InteractionInputData", menuName = "InteractionSystem/InputData")]
public class InteractionInputData : ScriptableObject
{
private bool m_interactedClicked;
private bool m_interactRelease;
public bool InteractedClicked
{
get => m_interactedClicked;
set => m_interactedClicked = value;
}
public bool InteractedReleased
{
get => m_interactRelease;
set => m_interactRelease = value;
}
public void Reset()
{
m_interactedClicked = false;
m_interactRelease = false;
}
}
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace th
{
public interface IInteractible
{
float HoldDurration { get; }
bool HoldInteract { get; }
bool MultipleUse { get;}
bool IsInteractible { get; }
void OnInteract();
}
}
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace th{
public class InteractableBase : MonoBehaviour,IInteractible
{
#region Variables
[Header("Interactible Settings")]
public float holdDuration;
[Space]
public bool holdInteract;
public bool multipleUse;
public bool isInteractible;
#endregion
#region Properties
public float HoldDurration => holdDuration;
public bool HoldInteract => holdInteract;
public bool MultipleUse => multipleUse;
public bool IsInteractible => isInteractible;
#endregion
#region Methods
public void OnInteract()
{
Debug.Log("Interacted: " + gameObject.name);
}
#endregion
}
}
Well without parsing through your entire code:
once
HoldingDownTimer >= 5
is true you still keep adding more time in the next frame so the condition is still true in the next frame => you will keep calling it all the following frames as well.
You could introduce a flag somewhat like e.g.
private bool alreadyCalled;
and then
if(interacting)
{
if(!alreadyCalled)
{
HoldingDownTimer += Time.DeltaTime;
if(HoldingDownTimer >= 5)
{
alreadyCalled = true;
//Do your task;
}
}
}
else
{
alreadyCalled = false;
}
As far as I understood your code I think:
I found the issue in it actually you are checking if the 'm_holdTimer' >= 'holdDuration' then perform interaction and you are making it equal to 0 on click start and click end.
So it means if the player has continuously held the button then it will not reset 'm_holdTimer'... so it will continue to call the function recursively because player hasn't actually ended the click so there is not any single function which is reseting 'm_holdTimer' to 0. still if you didn't get my point just copy this function and paste it instead of your own and see if it works, if it do work then you can check the difference between line of code then you'll understand it for sure.
void CheckForInteractableInput()
{
if (interactionData.isEmpty())
{
return;
}
if (interactionInputData.InteractedClicked)
{
m_interacting = true;
m_holderTimer = 0f;
}
if (interactionInputData.InteractedReleased)
{
m_interacting = false;
m_holderTimer = 0f;
}
if (m_interacting)
{
if (!interactionData.Interactable.IsInteractible)
return;
if (interactionData.Interactable.HoldInteract)
{
m_holderTimer += Time.deltaTime;
Debug.Log(m_holderTimer);
if (m_holderTimer >= interactionData.Interactable.holdDuration)
{
m_holderTimer = 0f;
interactionData.Interact();
m_interacting = false;
}
}
else
{
interactionData.Interact();
m_interacting = false;
}
}
}
Hope it helps... Happy coding :)
I was following a tutorial to make a game in unity C# and I need to know how I would add a High Score which persists among game restarts. I'm new to making games so any help is apreciated, and if I need to elaborate on anything please mention that.
public class UIManager : MonoBehaviour
{
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
DestroyImmediate(this);
}
}
private static UIManager instance;
public static UIManager Instance
{
get
{
if (instance == null)
instance = new UIManager();
return instance;
}
}
protected UIManager()
{
}
private float score = 0;
public void ResetScore()
{
score = 0;
UpdateScoreText();
}
public void SetScore(float value)
{
score = value;
UpdateScoreText();
}
public void IncreaseScore(float value)
{
score += value;
UpdateScoreText();
}
private void UpdateScoreText()
{
ScoreText.text = score.ToString();
}
public void SetStatus(string text)
{
StatusText.text = text;
}
public Text ScoreText, StatusText;
}
PlayerPrefs helps you to store game data.
Usage:
void SaveHighScore(float value)
{
PlayerPrefs.SetFloat("HighScore", value);
}
void LoadHighScroe()
{
float hs = PlayerPrefs.GetFloat("HighScore", 0.0f); // 0.0f here is default value when key/value not exest in PlayerPrefs
ScoreText.text = hs.ToString();
}
Or maybe to make the variables static and get reference to them in the editor.
This script sit in my Game scene.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SceneFader : MonoBehaviour
{
#region FIELDS
public GameObject fadeOutUIGameobjectImage;
public float fadeSpeed = 0.8f;
public bool loaded = false;
private static Image fadeOutUIImage;
private void Start()
{
}
public enum FadeDirection
{
In, //Alpha = 1
Out // Alpha = 0
}
#endregion
#region FADE
public static IEnumerator Fade(FadeDirection fadeDirection)
{
fadeOutUIGameobjectImage.SetActive(true);
float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
if (fadeDirection == FadeDirection.Out)
{
while (alpha >= fadeEndValue)
{
SetColorImage(ref alpha, fadeDirection);
yield return null;
}
fadeOutUIGameobjectImage.SetActive(false);
}
else
{
fadeOutUIGameobjectImage.SetActive(true);
while (alpha <= fadeEndValue)
{
SetColorImage(ref alpha, fadeDirection);
yield return null;
}
}
}
#endregion
#region HELPERS
public static IEnumerator FadeAndLoadSceneNewGame(FadeDirection fadeDirection, string sceneToLoad)
{
yield return Fade(fadeDirection);
loaded = false;
SceneManager.LoadScene(sceneToLoad);
}
public static IEnumerator FadeAndLoadSceneLoadGame(FadeDirection fadeDirection, string sceneToLoad)
{
yield return Fade(fadeDirection);
loaded = true;
SceneManager.LoadScene(sceneToLoad);
var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>();
saveLoad.Load();
}
private static void SetColorImage(ref float alpha, FadeDirection fadeDirection)
{
if(fadeOutUIImage == null)
{
fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
}
fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
}
#endregion
}
Now the variables in the tope are public but not static so I can't use them in the rest of the script and if I make them static I can't reference to them in the editor I tried to use Find in the Start when they are static but they are null.
And this script sit in my Main Menu scene I thought make the two methods in the Game scene public static will be easier to call them from the Main Menu scene :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEditor;
using Cinemachine;
using UnityStandardAssets.Characters.ThirdPerson;
public class MenuController : MonoBehaviour
{
#region Default Values
[Header("Default Menu Values")]
[SerializeField] private float defaultVolume;
[SerializeField] private int defaultSen;
[SerializeField] private bool defaultInvertY;
[Header("Levels To Load")]
public string _newGameButtonLevel;
private string levelToLoad;
public GameObject player;
private int menuNumber;
#endregion
#region Menu Dialogs
[Header("Main Menu Components")]
[SerializeField] private GameObject menuDefaultCanvas;
[SerializeField] private GameObject GeneralSettingsCanvas;
[SerializeField] private GameObject graphicsMenu;
[SerializeField] private GameObject soundMenu;
[SerializeField] private GameObject controlsMenu;
[SerializeField] private GameObject confirmationMenu;
[Space(10)]
[Header("Menu Popout Dialogs")]
[SerializeField] private GameObject noSaveDialog;
[SerializeField] private GameObject newGameDialog;
[SerializeField] private GameObject loadGameDialog;
#endregion
#region Slider Linking
[Header("Menu Sliders")]
[SerializeField] private Text controllerSenText;
[SerializeField] private Slider controllerSenSlider;
public float controlSenFloat = 2f;
[Space(10)]
[SerializeField] private Text volumeText;
[SerializeField] private Slider volumeSlider;
[Space(10)]
[SerializeField] private Toggle invertYToggle;
#endregion
#region Initialisation - Button Selection & Menu Order
private void Start()
{
menuNumber = 1;
}
#endregion
//MAIN SECTION
public IEnumerator ConfirmationBox()
{
confirmationMenu.SetActive(true);
yield return new WaitForSeconds(2);
confirmationMenu.SetActive(false);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (menuNumber == 2 || menuNumber == 7 || menuNumber == 8)
{
GoBackToMainMenu();
ClickSound();
}
else if (menuNumber == 3 || menuNumber == 4 || menuNumber == 5)
{
GoBackToOptionsMenu();
ClickSound();
}
else if (menuNumber == 6) //CONTROLS MENU
{
ClickSound();
}
}
}
private void ClickSound()
{
GetComponent<AudioSource>().Play();
}
#region Menu Mouse Clicks
public void MouseClick(string buttonType)
{
if (buttonType == "Controls")
{
controlsMenu.SetActive(true);
menuNumber = 6;
}
if (buttonType == "Graphics")
{
GeneralSettingsCanvas.SetActive(false);
graphicsMenu.SetActive(true);
menuNumber = 3;
}
if (buttonType == "Sound")
{
GeneralSettingsCanvas.SetActive(false);
soundMenu.SetActive(true);
menuNumber = 4;
}
if (buttonType == "Exit")
{
Debug.Log("YES QUIT!");
Application.Quit();
}
if (buttonType == "Options")
{
menuDefaultCanvas.SetActive(false);
GeneralSettingsCanvas.SetActive(true);
menuNumber = 2;
}
if (buttonType == "LoadGame")
{
menuDefaultCanvas.SetActive(false);
loadGameDialog.SetActive(true);
menuNumber = 8;
}
if (buttonType == "NewGame")
{
menuDefaultCanvas.SetActive(false);
newGameDialog.SetActive(true);
menuNumber = 7;
}
}
#endregion
public void VolumeSlider(float volume)
{
AudioListener.volume = volume;
volumeText.text = volume.ToString("0.0");
}
public void VolumeApply()
{
PlayerPrefs.SetFloat("masterVolume", AudioListener.volume);
Debug.Log(PlayerPrefs.GetFloat("masterVolume"));
StartCoroutine(ConfirmationBox());
}
public void ControllerSen()
{
controllerSenText.text = controllerSenSlider.value.ToString("0");
controlSenFloat = controllerSenSlider.value;
}
#region ResetButton
public void ResetButton(string GraphicsMenu)
{
if (GraphicsMenu == "Audio")
{
AudioListener.volume = defaultVolume;
volumeSlider.value = defaultVolume;
volumeText.text = defaultVolume.ToString("0.0");
VolumeApply();
}
if (GraphicsMenu == "Graphics")
{
controllerSenText.text = defaultSen.ToString("0");
controllerSenSlider.value = defaultSen;
controlSenFloat = defaultSen;
invertYToggle.isOn = false;
}
}
#endregion
#region Dialog Options - This is where we load what has been saved in player prefs!
public void ClickNewGameDialog(string ButtonType)
{
if (ButtonType == "Yes")
{
newGameDialog.SetActive(false);
StartCoroutine(SceneFader.FadeAndLoadSceneNewGame(SceneFader.FadeDirection.In, _newGameButtonLevel));
}
if (ButtonType == "No")
{
GoBackToMainMenu();
}
}
public void ClickLoadGameDialog(string ButtonType)
{
if (ButtonType == "Yes")
{
newGameDialog.SetActive(false);
StartCoroutine(SceneFader.FadeAndLoadSceneLoadGame(SceneFader.FadeDirection.In, _newGameButtonLevel));
}
if (ButtonType == "No")
{
GoBackToMainMenu();
}
}
#endregion
#region Back to Menus
public void GoBackToOptionsMenu()
{
GeneralSettingsCanvas.SetActive(true);
graphicsMenu.SetActive(false);
soundMenu.SetActive(false);
VolumeApply();
menuNumber = 2;
}
public void GoBackToMainMenu()
{
menuDefaultCanvas.SetActive(true);
newGameDialog.SetActive(false);
loadGameDialog.SetActive(false);
noSaveDialog.SetActive(false);
GeneralSettingsCanvas.SetActive(false);
graphicsMenu.SetActive(false);
soundMenu.SetActive(false);
menuNumber = 1;
}
public void ClickQuitOptions()
{
GoBackToMainMenu();
}
public void ClickNoSaveDialog()
{
GoBackToMainMenu();
}
#endregion
}
In this script I have two events :
ClickNewGameDialog and ClickLoadGameDialog that I'm calling them from OnClick buttons in the main menu.
I created this script and added it to an empty GameObject in the Game scene :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class NewGame : MonoBehaviour
{
// Here you store the actual instance
private static NewGame _instance;
// Public read-only access property
public static NewGame Instance
{
get
{
// if already set simply return directly
if (_instance) return _instance;
// Otherwise try to find it in the scene
_instance = FindObjectOfType<NewGame>();
if (_instance) return _instance;
// Otherwise create it now
_instance = new GameObject(nameof(NewGame)).AddComponent<NewGame>();
return _instance;
}
}
private bool _gameStarted;
public static bool GameStarted => Instance._gameStarted;
private void Awake()
{
if (_instance && _instance != this)
{
// There already exist another instance
Destroy(this.gameObject);
return;
}
// Otherwise this is the active instance and should not be destroyed
_instance = this;
DontDestroyOnLoad(this.gameObject);
SceneManager.sceneLoaded += OnSceneLoaded;
// update it once now
_gameStarted = SceneManager.GetActiveScene().buildIndex != 0;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
_gameStarted = scene.buildIndex != 0;
}
}
but when I tried to use it for exmaple in the Game scene like this :
if(NewGame.GameStarted != true)
GameStarted is all the time true never false so it's all the time staring a new game even if the player clicked the Load Game button.
Use Singletone Design Pattern. Create a static variable that refers to your script like this:
public static YourCodeBehaviour Instance;
Then in Awake method you just check if it null and set it if true, like this.
private void Awake() {
if(Instance == null)
Instance = this;
else {
Destroy(gameObject);
}
}
Then you got an Instance that you can access from your static methods, cause Instance is static and got the actual alive instance.
Good luck!
I've just updated to the new Input System from 2.7 to 2.8.
How the new Input System works is you create an input actions asset by going to Create-> Input Actions
.
This creates an asset where actions can be mapped to keys. One then create a C# script from this asset and use it in their code. Which is what I did. I called the Asset MyInput.inputactions and the C# script is MyInput.cs
When you use the generated C# script this way you need to reference the asset in your script. However, after the update, it seems this is impossible to do from the editor. When I define a public MyInput variable in my class, like so:
public class ShapeMover: MonoBehaviour
{
public MyInput controls;
private float _lastFallTime;
private float _fallSpeed;
private ShapeSpawner _spawn;
private GameObject _shapeToMove;
private Transform _shapeToMoveTransform;
private bool _isGameOver;
private const float _leftRotationAngle = (float) -1.57079633;
private const float _rightRotationAngle = (float) 1.57079633;
}
It isn't exposed in the inspector:
And I get an obvious NullReferenceExceptionerror when I try to access the controls variable.
Am I doing something wrong?
How can I reference the asset from the inspector? I have tried adding [SerializeField] to the public declaration, it didn't help.
I was following this video and it worked fine until I updated to a newer Input System version.
For reference, this is the full ShapeMover class:
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace _Scripts
{
public class ShapeMover : MonoBehaviour
{
[SerializeField]
public MyInput controls;
private float _lastFallTime;
private float _fallSpeed;
private ShapeSpawner _spawn;
private GameObject _shapeToMove;
private Transform _shapeToMoveTransform;
private bool _isGameOver;
private const float _leftRotationAngle = (float) -1.57079633;
private const float _rightRotationAngle = (float) 1.57079633;
private void Awake()
{
_spawn = FindObjectOfType<ShapeSpawner>();
_lastFallTime = 0f;
_fallSpeed = GameGrid.Instance.GetFallSpeed();
_isGameOver = false;
Debug.Log("Registering controls callbacks...");
controls.Player.Movement.performed += ctx => Movement(ctx.ReadValue<Vector2>(), true);
controls.Player.Drop.performed += ctx => Drop();
controls.Menu.Reset.performed += ctx => Restart();
controls.Menu.Pause.performed += ctx => PauseToggle();
SetShapeToMove();
}
private void Restart()
{
GameGrid.Instance.ResetGame();
_isGameOver = false;
SetShapeToMove();
}
private void PauseToggle()
{
Debug.Log("Got Pause input");
var currentPauseState = GameGrid.Instance.IsPaused;
//If not paused, will pause
if (!currentPauseState)
{
// controls.Player.Movement.Disable();
// controls.Player.Drop.Disable();
// controls.Player.Menu.Disable();
// controls.Player.Disable();
GameGrid.Instance.IsPaused = true;
}
else
{
// controls.Player.Movement.Enable();
// controls.Player.Drop.Enable();
// controls.Player.Menu.Enable();
// controls.Player.Enable();
GameGrid.Instance.IsPaused = false;
}
}
private void Drop()
{
// Debug.Log("Should Drop Shape!");
bool didMove = true;
while (didMove)
{
didMove = Movement(new Vector2(0, -1), false);
}
}
private bool Movement(Vector2 direction, bool isFromInput)
{
if (isFromInput)
{
Debug.Log($"Got input {direction.ToString()}");
}
//Disable movement controls when game is over.
if (_isGameOver)
{
return false;
}
var oldPosition = _shapeToMoveTransform.position;
var oldRotation = _shapeToMoveTransform.rotation;
// Transform[] children = _shapeToMoveTransform.Cast<Transform>().ToArray();
var didMove = true;
var didEndMovement = false;
GameGrid.Instance.RemoveShapeFromGrid(_shapeToMoveTransform);
if (direction.x < 0)
{
didMove = MoveLeft();
}
else if (direction.x > 0)
{
didMove = MoveRight();
}
else if (direction.y > 0)
{
didMove = RotateLeft();
}
else if (direction.y < 0)
{
didMove = MoveDown();
if (!didMove)
{
didEndMovement = true;
}
}
//If Shape didn't move, restore previous position.
if (!didMove)
{
_shapeToMoveTransform.position = oldPosition;
_shapeToMoveTransform.rotation = oldRotation;
}
GameGrid.Instance.AddShapeToGrid(_shapeToMoveTransform);
// Debug.Log($"Shape {_shapeToMove.name} Position after movement Did Move: {didMove.ToString()}");
// Transform[] children = _shapeToMoveTransform.Cast<Transform>().ToArray();
// var lowestChild = children.OrderBy(x => x.position.y).First();
// Debug.Log($"{lowestChild.position.ToString()}");
if (didEndMovement)
{
GameGrid.Instance.ClearRows(_shapeToMoveTransform);
_isGameOver = GameGrid.Instance.IsGameOver(_shapeToMoveTransform);
if (!_isGameOver)
{
SetShapeToMove();
}
}
return didMove;
}
private void SetShapeToMove()
{
_shapeToMove = _spawn.SpawnShape();
_shapeToMoveTransform = _shapeToMove.transform;
}
private void Update()
{
if (_isGameOver)
{
return;
}
if (GameGrid.Instance.IsPaused)
{
return;
}
var time = Time.time;
if (!(time - (_lastFallTime + _fallSpeed) > 0))
{
return;
}
Movement(new Vector2(0, -1), false);
_lastFallTime = time;
_fallSpeed = GameGrid.Instance.GetFallSpeed();
}
private bool MoveLeft()
{
_shapeToMoveTransform.position += Vector3.right;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool MoveRight()
{
_shapeToMoveTransform.position += Vector3.left;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool MoveDown()
{
_shapeToMoveTransform.position += Vector3.down;
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private bool RotateLeft()
{
_shapeToMoveTransform.Rotate(0, 0, -90);
// foreach (Transform child in _shapeToMoveTransform)
// {
// RotateTransform(child, _leftRotationAngle);
// }
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private void RotateTransform(Transform transformToRotate, float rotationAngleRadian)
{
var currentLocalPosition = transformToRotate.localPosition;
var currentX = currentLocalPosition.x;
var currentY = currentLocalPosition.y;
var rotatedX = currentX * Mathf.Cos(rotationAngleRadian) - currentY * Mathf.Sin(rotationAngleRadian);
var rotatedY = currentX * Mathf.Sin(rotationAngleRadian) + currentY * Mathf.Cos(rotationAngleRadian);
transformToRotate.localPosition = new Vector2(rotatedX, rotatedY);
// Debug.Log($"Position after rotation is: {transformToRotate.localPosition.ToString()}");
}
private bool RotateRight()
{
_shapeToMoveTransform.Rotate(0, 0, -90);
return GameGrid.Instance.CanMove(_shapeToMoveTransform);
}
private void OnEnable()
{
Debug.Log("Controls Enabled...");
controls.Enable();
}
// private void OnDisable()
// {
// Debug.Log("Controls Disabled...");
// controls.Disable();
// }
}
}
Just as you said, you can't reference the new generated input class anymore.
To make it works, i instantiated the class, and use the SetCallbacks method, like this :
private MyInput _inputs;
public void Awake()
{
_inputs = new MyInput();
}
Truth be told, i don't know if it's the intended way of using the input class, but it works.
EDIT :
Starting from the 2.8 preview, an interface is automatically generated. I can only recommend it, cause it's very easy to use, you just need to inherits from IYourActionsSetNameActions and add the callbacks. (Also, you have to enable / disable the actions set, but you should be able to do it in another script)
Here is a complete base example, using your naming :
public class ShapeMover : MonoBehaviour, MyInput.IPlayerActions
{
private MyInput _inputs;
public void Awake()
{
_inputs = new MyInput();
_inputs.Player.SetCallbacks(this);
}
public void OnEnable()
{
_inputs.Player.Enable();
}
public void OnDisable()
{
_inputs.Player.Disable();
}
public void OnMovement(InputAction.CallbackContext context)
{
Vector2 delta = context.ReadValue<Vector2>();
transform.position += new Vector3(delta.x, 0, delta.y);
}
public void OnDrop(InputAction.CallbackContext context)
{
//TODO
}
// ...
}
In Unity:
Could anybody tell me what I did wrong. I wanted to pop up a dialogue after you are nearby a charecter but somehow my code doesn't really work.
public class Interactable : MonoBehaviour {
[HideInInspector]
public NavMeshAgent playerAgent;
private bool hasInteracted;
public virtual void MoveToIneraction(NavMeshAgent playerAgent)
{
hasInteracted = false;
this.playerAgent = playerAgent;
playerAgent.stoppingDistance = 2.3f;
playerAgent.destination = this.transform.position;
Interact ();
}
void Update()
{
if (!!hasInteracted && playerAgent != null && playerAgent.pathPending)
{
if(playerAgent.remainingDistance <= playerAgent.stoppingDistance)
{
Interact();
hasInteracted = true;
}
}
}
public virtual void Interact()
{
Debug.Log("Interacted");
}
}
!!hasInteracted
It should be !hasInteracted, I guess