I have a boolean variable which I set to true when I want certain parts of my scene to move up. The problem is, it seems to be resetting to false every time I set it to true, during the same frame. I've made the bool into a property and set a breakpoint in the set method and it only ever gets called with "true" as a parameter.
Is there something I'm doing wrong, or is Unity doing some weird stuff behind?
private bool SceneMoveUp
{
get
{
return _sceneMoveUp;
}
set
{
_sceneMoveUp = value;
}
}
void Update () {
if (SceneMoveUp == true) {
transform.position = Vector3.MoveTowards(transform.position, SceneDestination, speed * Time.deltaTime);
}
}
EDIT: SceneMoveUp is a property inside a MonoBehaviour-derived class.
EDIT2: entire code
using UnityEngine;
using System.Collections;
public class PseudoScene : MonoBehaviour {
public Vector3 ZoomOutPosition;
public GameObject[] Letters;
public Vector3[] CameraPositions;
public PseudoSceneManager _manager;
public float speed;
private float DepartureTime;
private Vector3 TextureOffset;
private int LetterCount, CurrentIndex = 0;
private bool ZoomingOut = false;
private bool _sceneMoveUp;
private bool SceneMoveUp{ get
{
return _sceneMoveUp;
}
set
{
_sceneMoveUp = value;
}
}
static public bool MovingCamera = false;
private Vector3 BackgroundFinalScale;
private Vector3 SceneDestination;
[SerializeField]private Quaternion FinalRotation;
void Start () {
LetterCount = Letters.Length;
SceneMoveUp = false;
if (LetterCount == 1) {
CameraPositions = new Vector3[1];
CameraPositions[0] = transform.position;
CameraPositions[0].z = -10;
}
ChangeLetter();
}
public void LetterFilled () {
CurrentIndex++;
if (CurrentIndex < LetterCount) {
ChangeLetter();
} else {
if (LetterCount > 1) {
ZoomOut();
} else {
MoveScene();
}
Invoke("FinishScene", 3f);
}
}
void Update () {
if (ZoomingOut) {
Camera.main.transform.position = Vector3.Lerp(transform.position, ZoomOutPosition, 1f);
Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, 13, 0.1f);
}
if (MovingCamera) {
Camera.main.transform.position = Vector3.MoveTowards(Camera.main.transform.position, CameraPositions[CurrentIndex], speed * Time.deltaTime);
if (Camera.main.transform.position == CameraPositions[CurrentIndex]) {
MovingCamera = false;
Checkpoint.DeactivateAllCheckpoints(true);
if (LetterCount > 1 && CurrentIndex >= 1) {
Checkpoint.DeactivateAllCheckpoints(true);
}
}
}
if (SceneMoveUp == true) {
transform.position = Vector3.MoveTowards(transform.position, SceneDestination, speed * Time.deltaTime);
}
}
void MoveScene () {
DepartureTime = Time.time;
SceneMoveUp = true;
Debug.logger.Log("set SceneMoveUp to ", SceneMoveUp.ToString());
SceneDestination = transform.position;
SceneDestination.y += 10;
}
public void FinishScene () {
_manager.SceneFinished();
}
//5.75
void ChangeLetter () {
Checkpoint.DeactivateAllCheckpoints(false);
MovingCamera = true;
}
void ZoomOut () {
ZoomingOut = true;
}
}
It seems there was a random bug which made it not work. I deleted the class, created a new one with the exact same code (copy-paste) and it now works. Thanks everyone for trying.
As crazy as this sounds, I had the exact same problem. After hours of trying to debug, I copy-pasted the exact code into a new class. Bam! It worked. I assume that I did something to cause the error and wish I new what it was. But copy-paste worked.
Related
i have a camera shake script but when i click start it directy goes off and it wont stop
in this script i wanted to trigger the shake effect
public class GameOver : MonoBehaviour
{
public Explosion shake;
void OnCollisionEnter2D (Collision2D coll)
{
Debug.Log("Collision!");
if (coll.gameObject.tag == "platform")
{
Destroy(gameObject);
shake.GameOver = true;
}
}
}
and this is the other script
public class Explosion : MonoBehaviour
{
public Transform cameraTransform;
private Vector3 orignalCameraPos;
public float shakeDuration;
public float shakeAmount;
private bool canShake = false;
public bool GameOver = false;
private float _shakeTimer;
void Start()
{
orignalCameraPos = cameraTransform.localPosition;
GameOver = false;
_shakeTimer = 0;
}
void Update()
{
if (GameOver = true)
{
ShakeCamera();
}
}
public void ShakeCamera()
{
_shakeTimer = shakeDuration;
StartCameraShakeEffect();
}
public void StartCameraShakeEffect()
{
if (_shakeTimer > 0)
{
cameraTransform.localPosition = orignalCameraPos + Random.insideUnitSphere * shakeAmount;
_shakeTimer -= Time.deltaTime;
}
else
{
_shakeTimer = 0f;
cameraTransform.position = orignalCameraPos;
GameOver = false;
}
}
}
if you are able to help me i would be very thankfull :)
in the statement if (GameOver = true) you are setting GameOver to true and then checking its value. I think you might have wanted to use if (GameOver == true) or if (GameOver) which will check if this variable is set to true.
I am rewriting my player movement script to make use of hierarchical state machines (mostly to get the practice and learn how they work) as per this tutorial.
I am beginning to understand how the state machine works, but having some trouble making changes that I'd like to make. Specifically, I want to make the PlayerJumpState a substate that gets called if the PlayerAirborneState is switched to and the jump button was pressed. The problem is I can't seem to figure out a way to switch to the jump as a substate while maintaining the airborne state as the superstate.
I have a PlayerGroundedState which functions as it should - it initializes the necessary substates, and the player is switched to these substates when the right conditions are met, meanwhile, the superState they are in can be either grounded or airborne. The jump state I want to use should only be accessible by the airborne state, and it should maintain the airborne superstate when switched to.
Here's the PlayerGroundedState script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerGroundedState : PlayerBaseState
{
public PlayerGroundedState(PlayerStateMachine currentContext, PlayerStateFactory playerStateFactory)
: base(currentContext, playerStateFactory)
{
IsRootState = true;
InitializeSubstate();
}
public override void EnterState()
{
Debug.Log("Grounded State Entered");
Ctx.CharacterController.stepOffset = Ctx.OrigStepOffset;
Ctx.Animator.SetBool("isGrounded", true);
Ctx.Animator.SetBool("isJumping", false);
Ctx.YSpeed = -1;
}
public override void UpdateState()
{
if (Ctx.IsJumpPressed)
{
Ctx.WasJumpPressed = true;
}
CheckSwitchStates();
}
public override void ExitState() { }
public override void InitializeSubstate()
{
if (!Ctx.IsMovementPressed)
{
SetSubState(Factory.Idle());
}
else if (Ctx.IsMovementPressed)
{
SetSubState(Factory.Walk());
}
}
public override void CheckSwitchStates()
{
if (Ctx.IsJumpPressed)
{
SwitchState(Factory.Airborne());
}
}
void OnAnimatorMove()
{
Vector3 velocity = Ctx.Animator.deltaPosition;
velocity.y = Ctx.YSpeed * Time.deltaTime;
Ctx.CharacterController.Move(velocity);
}
}
My PlayerAirborneState script is currently set up the same way as PlayerGroundedState, but obviously isn't even switching the substate like I was hoping. If I use the SwitchState function in CheckSwitchStates() it will switch to the jump state, but doing that does not maintain airborne as the superstate. I expect that some of this might have to do with the fact that in my PlayerStateMachine script, the grounded state is initialized as the first current state.
PlayerAirborneState:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAirborneState : PlayerBaseState
{
public PlayerAirborneState(PlayerStateMachine currentContext, PlayerStateFactory playerStateFactory)
: base(currentContext, playerStateFactory)
{
IsRootState = true;
}
public override void EnterState()
{
Debug.Log("Airborne State Entered");
Ctx.Animator.SetBool("isJumping", true);
InitializeSubstate();
}
public override void UpdateState()
{
HandleAirMovement();
CheckSwitchStates();
}
public override void ExitState()
{
Debug.Log("Airborne State exited");
Ctx.Animator.SetBool("isJumping", false);
}
public override void InitializeSubstate()
{
if (Ctx.WasJumpPressed || Ctx.YSpeed > 0)
{
SetSubState(Factory.Jump());
}
else
{
SetSubState(Factory.Falling());
}
}
public override void CheckSwitchStates()
{
if (Time.time - Ctx.LastGroundedTime <= Ctx.JumpButtonGracePeriod)
{
SwitchState(Factory.Grounded());
}
//if (Ctx.WasJumpPressed || Ctx.YSpeed > 0)
//{
// SwitchState(Factory.Jump());
//}
//else
//{
// SwitchState(Factory.Falling());
//}
}
void HandleAirMovement()
{
Ctx.LastGroundedTime = null;
if (Ctx.WasJumpPressed)
{
Ctx.YSpeed = Ctx.JumpSpeed;
}
Vector3 airVelocity;
airVelocity = Ctx.BetterMoveVector.normalized * Ctx.InputMagnitude * Ctx.JumpHorizontalSpeed;
airVelocity.y = Ctx.YSpeed;
Ctx.CharacterController.Move(airVelocity * Time.deltaTime);
}
}
And my PlayerStateMachine script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerStateMachine : MonoBehaviour
{
// input booleans (iHeartGameDev tutorial)
bool _isJumpPressed = false;
bool _isMovementPressed;
// constants (iHeartGameDev)
float _rotationFactorPerFrame = 15;
public ParticleSystem jetFire1;
public ParticleSystem jetFire2;
public PlayerInventory playerInventory;
[SerializeField] float _rotationSpeed;
[SerializeField] float _turnSmoothTime;
[SerializeField] float _jumpSpeed;
[SerializeField] float _jumpButtonGracePeriod;
[SerializeField] float _jetForce;
[SerializeField] float _jumpHorizontalSpeed;
[SerializeField] float _boostSpeed;
[SerializeField] float _boostTime;
[SerializeField] float _wallJumpBuffer;
[SerializeField] float _wallJumpForce;
[SerializeField] Transform _cameraTransform;
CharacterController _characterController;
Animator _animator;
CapsuleCollider _capsuleCollider;
float _ySpeed;
float _inputMagnitude;
float? _jumpButtonPressedTime;
float? _lastGroundedTime;
float _origStepOffset;
float _wallJumpStartTime;
bool _touchingWall;
bool _wallJumpStarted;
bool _raycastIsGrounded;
bool _wasJumpPressed;
Vector2 _moveVector2;
Vector3 _betterMoveVector;
Vector3 _moveDirection;
Vector3 _wallJumpNormal;
//Vector3 _airVelocity;
// state variables
PlayerBaseState _currentState;
PlayerStateFactory _states;
// getters and setters
public PlayerBaseState CurrentState { get { return _currentState; } set { _currentState = value; } }
public CharacterController CharacterController { get { return _characterController; } }
public Animator Animator { get { return _animator; } }
public CapsuleCollider CapsuleCollider { get { return _capsuleCollider; } }
public float JumpButtonGracePeriod { get { return _jumpButtonGracePeriod; } }
public float YSpeed { get { return _ySpeed; } set { _ySpeed = value; } }
public float JumpSpeed { get { return _jumpSpeed; } }
public float OrigStepOffset { get { return _origStepOffset; } }
public float InputMagnitude { get { return _inputMagnitude; } }
public float JumpHorizontalSpeed { get { return _jumpHorizontalSpeed; } }
public float? JumpButtonPressedTime { get { return _jumpButtonPressedTime; } set { _jumpButtonPressedTime = value; } }
public float? LastGroundedTime { get { return _lastGroundedTime; } set { _lastGroundedTime = value; } }
public bool IsJumpPressed { get { return _isJumpPressed; } }
public bool WasJumpPressed { get { return _wasJumpPressed; } set { _wasJumpPressed = value; } }
public bool IsMovementPressed { get { return _isMovementPressed; } }
public bool RaycastIsGrounded { get { return _raycastIsGrounded; } }
public Vector2 MoveVector2 { get { return _moveVector2; } set { _moveVector2 = value; } }
public Vector3 BetterMoveVector { get { return _betterMoveVector; } set { _betterMoveVector = value; } }
//public Vector3 AirVelocity { get { return _airVelocity; } set { _airVelocity = value; } }
private void Awake()
{
_characterController = GetComponent<CharacterController>();
_animator = GetComponent<Animator>();
_capsuleCollider = GetComponent<CapsuleCollider>();
_ySpeed = -1;
// setup state
_states = new PlayerStateFactory(this);
_currentState = _states.Grounded();
_currentState.EnterState();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
_currentState.UpdateStates();
//_ySpeed += (Physics.gravity.y * Time.deltaTime);
RaycastGroundCheck();
_animator.SetFloat("Input Magnitude", _inputMagnitude, 0.05f, Time.deltaTime);
// set movement vector to inputs
_betterMoveVector.x = MoveVector2.x;
_betterMoveVector.z = MoveVector2.y;
HandleRotation();
}
void HandleRotation()
{
// move in direction of camera
_betterMoveVector = Quaternion.AngleAxis(_cameraTransform.rotation.eulerAngles.y, Vector3.up) * _betterMoveVector;
_betterMoveVector.Normalize();
// the current rotation of our character
Quaternion currentRotation = transform.rotation;
if (_isMovementPressed)
{
// creates a new rotation based on where the player is currently pressing
Quaternion targetRotation = Quaternion.LookRotation(_betterMoveVector, Vector3.up);
// rotate the character to face the positionToLookAt
transform.rotation = Quaternion.Slerp(currentRotation, targetRotation, _rotationFactorPerFrame * Time.deltaTime);
}
}
void RaycastGroundCheck()
{
float extraHeight = 0.1f;
if (Physics.Raycast(_capsuleCollider.bounds.center, Vector3.down, CapsuleCollider.bounds.extents.y + extraHeight))
{
_raycastIsGrounded = true;
}
else
{
_raycastIsGrounded = false;
}
}
//input functions
public void JumpFunction(InputAction.CallbackContext context)
{
_isJumpPressed = context.ReadValueAsButton();
_jumpButtonPressedTime = Time.time;
}
public void MoveFunction(InputAction.CallbackContext context)
{
if (context.performed || context.started)
{
Debug.Log("Movement!");
_moveVector2 = context.ReadValue<Vector2>();
//Debug.Log("_moveVector2: " + _moveVector2);
_inputMagnitude = _moveVector2.magnitude;
_isMovementPressed = _inputMagnitude != 0;
_moveVector2.Normalize();
}
else
{
Debug.Log("movement exited");
_moveVector2 = Vector2.zero;
_isMovementPressed = false;
}
}
public void JetUpFunction(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Jet Up performed");
}
}
public void JetBoostFunction(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Forward Boost performed");
}
}
public void DebugCurrentStates(InputAction.CallbackContext context)
{
if (context.performed)
{
Debug.Log("Current State: " + _currentState);
}
}
}
This is my first Unity project as well as my first question on SO, so apologies for my newbiness on both accounts.
Today I'm coming to you because I have a weird StackOverflow Exception and do not know how to fix it at all...
First off, this seems to only happen on windows after I build the game.
This is what I see in the output_log.txt :
onMoneyChanged is being called! (4145)
UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
Player:set_Money(Int32) (at /Users/Name/Desktop/My Game/Assets/Scripts/Mobs/Player.cs:89)
Coin:OnPickup(ItemCollector) (at /Users/Name/Desktop/My Game/Assets/Scripts/Items/Coin.cs:12)
ItemCollector:Update() (at /Users/Name/Desktop/My Game/Assets/Scripts/Items/ItemCollector.cs:35)
(Filename: /Users/Name/Desktop/My Game/Assets/Scripts/Mobs/Player.cs Line: 89)
onMoneyChanged is being called! (4150)
UnityEngine.DebugLogHandler:Internal_Log(LogType, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
Player:set_Money(Int32) (at /Users/Name/Desktop/My Game/Assets/Scripts/Mobs/Player.cs:89)
Coin:OnPickup(ItemCollector) (at /Users/Name/Desktop/My Game/Assets/Scripts/Items/Coin.cs:12)
ItemCollector:Update() (at /Users/Name/Desktop/My Game/Assets/Scripts/Items/ItemCollector.cs:35)
(Filename: /Users/Name/Desktop/My Game/Assets/Scripts/Mobs/Player.cs Line: 89)
Uploading Crash Report
StackOverflowException: The requested operation caused a stack overflow.
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
at (wrapper delegate-invoke) System.Action:invoke_void__this__ ()
I have looked everywhere and can't seem to understand where it comes from. I might not be seeing something very simple...
Here is the player script:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Events;
using PixelUtilities;
public class Player : Mob, IUpgradable, IXmlSerializable {
#region Static Section
private static readonly AnimationParameter Skill2SpeedId = "Skill 2 Speed";
private static readonly string[] AttackAxisNames = new string[] {
null,
"Attack1",
"Attack2",
"Attack3"
};
//It seems this must be longer in duration than the transition into the attack state, due to the potential transition interruption sources!
private const float AttackUnjoggableTime = 0.65f;
#endregion
[Header("Player")]
[SerializeField] private float skill2Speed = 1;
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundCheckRadius = 0.25f;
[SerializeField] private LayerMask groundLayers;
[SerializeField] private bool isGrounded;
[SerializeField] GameObject weaponSpecial;
private new Rigidbody2D rigidbody;
private new BoxCollider2D collider;
private float horizontal;
private float smoothedHorizontal;
private Vector3 velocity;
private int superCharge; //Represents the number of enemies defeated that counts towards allowing the player to use their super attack.
private bool canIncreaseSuperCharge = true;
private bool canSuperSmash;
private int money = 0;
//private int roundMoney = 0;
private int[] levels = new int[2];
//DO NOT show in the inspector. That will make it changeable without actually updating the data
//that needs to change based based on this upgradeLevel.
private int baseDamage;
private float timeLastAttacked;
public event Action onMoneyChanged;
public bool CanSuperSmash {
get { return canSuperSmash; }
set {
//Prevent from redundant setting, because we'll need to know
//just when the value changed from false to true
if (canSuperSmash == value)
return;
canSuperSmash = value;
weaponSpecial.SetActive(value);
if (value)
superCharge = GameManager.Mage.SpecialEnemyCount;
}
}
public BoxCollider2D Collider {
get { return collider; }
}
public int SuperCharge {
get { return superCharge; }
}
public int LevelCount {
get { return levels.Length; }
}
public int Money {
get { return money; }
set {
if (GameManager.IsDebugMode)
Debug.Log("Setting the player's money from " + money + " to " + value + ".");
money = value;
Debug.Log("onMoneyChanged is being called! (" + money + ")");
if (onMoneyChanged != null)
onMoneyChanged();
}
}
//public int RoundMoney {
// get { return roundMoney; }
// set {
// roundMoney = value;
// Debug.Log("roundMoney has been set to " + roundMoney + ".");
// }
//}
public override void Reset() {
base.Reset();
groundLayers = LayerMask.GetMask("Ground");
}
public override void Awake() {
base.Awake();
collider = GetComponentInChildren<BoxCollider2D>();
HPStatus.onDeath += OnDeath;
baseDamage = StandTallCurves.GetNthStepInEnemyHealthCurve(0) / 2;
}
public override void Start() {
rigidbody = GetComponent<Rigidbody2D>();
}
public int GetLevel(int levelIndex) {
return levels[levelIndex];
}
public void SetLevel(int levelIndex, int value) {
value = Mathf.Max(0, value);
levels[levelIndex] = value;
switch (levelIndex) {
case 0:
baseDamage = StandTallCurves.GetNthStepInEnemyHealthCurve(value) / 2;
break;
case 1:
HPStatus.HP = HPStatus.MaxHP = StandTallCurves.GetNthStepInEnemyHealthCurve(value);
break;
}
}
public override void StartAttack(int attackNumber) {
base.StartAttack(attackNumber);
timeLastAttacked = Time.time;
}
protected override void OnUpdate() {
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayers);
if (CanPerformActions)
AcceptAttackInput();
AcceptMovementInput();
//AcceptJumpInput();
//AcceptRotationalInput();
if (GameManager.IsDeveloperMode) {
if (Input.GetKeyDown(KeyCode.C)) {
Money += 1000;
}
if (Input.GetKeyDown(KeyCode.Y)) {
CanSuperSmash = true;
}
if (Input.GetKeyDown(KeyCode.T) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftCommand))) {
Time.timeScale = (Time.timeScale == 1) ? 5 : 1;
}
}
}
private void AcceptMovementInput() {
horizontal = hInput.GetAxis("Horizontal");
//smoothedHorizontal = Input.GetAxis("Horizontal");
velocity = rigidbody.velocity;
velocity.x = Mathf.Sign(horizontal) * FinalMovementSpeed;
rigidbody.velocity = velocity;
//Section for updating their rotation
if (horizontal < 0) {
Quaternion newRotation = transform.rotation;
newRotation.y = 180;
transform.rotation = newRotation;
}
if (horizontal > 0) {
Quaternion newRotation = transform.rotation;
newRotation.y = 0;
transform.rotation = newRotation;
}
}
private void AcceptAttackInput() {
for (int i = 1; i <= 2; i++) {
if (hInput.GetButtonDown(AttackAxisNames[i])) {
StartAttack(i);
return;
}
}
//Attack 3 is special for the player -- their super smash -- and is handled differently.
if (hInput.GetButtonDown(AttackAxisNames[3])) {
if (canSuperSmash) {
StartSuperSmash();
} else {
if (GameManager.IsDebugMode) {
Debug.Log("Attempted to super smash, but " + name + " was unable to super smash!");
}
}
}
}
protected override float CalculateFinalMovementSpeed() {
float finalMovementSpeed = Mathf.Abs(horizontal) * MoveSettings.MoveSpeed;
finalMovementSpeed *= MovementFactor;
return finalMovementSpeed;
}
protected override void UpdateContinuousAnimatorParameters() {
animator.SetBool(Animations.IsMovingId, Time.time - timeLastAttacked > AttackUnjoggableTime && Mathf.Abs(horizontal) > 0.08f && Mathf.Abs(velocity.x) > 0.08f);
//animator.SetFloat(Skill2SpeedId, skill2Speed);
}
/* Uncomment to enable jumping
private void AcceptJumpInput() {
if (Input.GetButtonDown("Jump") && isGrounded) {
rigidbody.velocity = new Vector3(rigidbody.velocity.x, JumpFactor * MoveSettings.JumpSpeed, 0);
if (JumpFactor > 0)
animator.SetTrigger(JumpId);
}
}*/
/// <summary>
/// This tells the whether or not the super charge count can be increased.
/// </summary>
public void SetSuperChargeActive(bool value) {
canIncreaseSuperCharge = value;
}
public bool GainSuperCharge() {
return GainSuperCharge(1);
}
public bool GainSuperCharge(int amount) {
//If they're already charged up, then don't allow superCharge to increment
if (!GameManager.Mage.gameObject.activeSelf || !canIncreaseSuperCharge || canSuperSmash)
return false;
superCharge = Mathf.Clamp(superCharge + amount, 0, GameManager.Mage.SpecialEnemyCount);
if (superCharge == GameManager.Mage.SpecialEnemyCount) {
CanSuperSmash = true; //Important to call the C# property here, NOT directly access the field, "canSuperSmash".
}
return true;
}
private void StartSuperSmash() {
SetSuperChargeActive(false);
CanSuperSmash = false;
StartCoroutine(AttackAfterDelay(3, 0)); //0 was initially 0.8f;
}
public void ClearSuperCharge() {
if (GameManager.IsDebugMode)
Debug.Log("superCharge set to 0!");
superCharge = 0;
}
private IEnumerator AttackAfterDelay(int attackNumber, float initialDelay) {
if (initialDelay > 0)
yield return new WaitForSeconds(initialDelay);
StartAttack(attackNumber);
}
public override void OnDrawGizmosSelected() {
base.OnDrawGizmosSelected();
if (groundCheck != null) {
Gizmos.color = new Color(0.6f, 1, 0, 1);
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}
attackChecker.DrawGizmos(Color.red);
}
private void InstantKillAllInView() {
Camera camera = Camera.main;
Vector3 cameraPos = camera.transform.position;
float height = 2 * camera.orthographicSize;
float width = height * camera.aspect; //h * (w/h) = w
RaycastHit2D[] hits = Physics2D.BoxCastAll((Vector2) cameraPos, new Vector2(width, height), 0, Vector2.right, 0.01f, enemyLayers, -0.01f, 0.01f);
for (int i = 0; i < hits.Length; i++) {
Mob target = hits[i].transform.GetComponent<Mob>();
if (target == null)
continue;
target.InstantSuperKill(this);
}
}
public void DamageOthersInRangeFromPlayer() {
DamageOthersInRange(baseDamage);
}
private void OnDeath(DamageInfo finalDamage) {
GameManager.StartGameOverScreen();
}
public void Load(XElement element) {
if (element == null)
return;
int intValue;
XElement child = element.Element("Money");
if (child != null && int.TryParse(child.Value, out intValue))
Money = intValue;
child = element.Element("HealthUpgrade");
if (child != null) {
//Old serialized layout
//if (int.TryParse(child.Value, out intValue))
// HealthUpgradeLevel = intValue;
//child = element.Element("DamageUpgrade");
//if (child != null && int.TryParse(child.Value, out intValue))
// DamageUpgradeLevel = intValue;
} else {
//New serialized layout
child = element.Element("Levels");
if (child != null)
LoadLevels(child, ref levels);
}
}
public XElement Save() {
XElement element = new XElement(GetType().Name);
element.Add(new XElement("Money", money));
//e.Add(new XElement("HealthUpgrade", healthUpgradeLevel));
//e.Add(new XElement("DamageUpgrade", damageUpgradeLevel));
element.Add(SaveLevels(ref levels));
return element;
}
#region Public Animator Methods
public void AnimatorAllowSuperSmashToRestart() {
ClearSuperCharge();
SetSuperChargeActive(true);
}
#endregion
}
Here is the Coin script:
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class Coin : MonoBehaviour, IItem {
[SerializeField] private int moneyAmount = 5;
public void OnValidate() {
moneyAmount = Mathf.Max(0, moneyAmount);
}
public void OnPickup(ItemCollector collector) {
collector.Owner.Money += moneyAmount;
//collector.Owner.RoundMoney += moneyAmount;
}
}
And finally, the itemCollector script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemCollector : MonoBehaviour {
[SerializeField] private float radius = 0.5f;
[SerializeField] private LayerMask itemMask;
private Player owner;
private Collider2D[] results = new Collider2D[16];
private int resultsCount;
public Player Owner {
get { return owner; }
}
public void Awake() {
owner = GetComponentInParent<Player>();
if (owner == null) {
if (GameManager.IsDebugMode) {
Debug.LogError(" require a " + typeof(Player).Name + " to be in a parent!" +
"\nThe " + GetType().Name + " will be destroyed, as it would not be able to function.");
}
DestroyImmediate(gameObject);
}
}
public void Update() {
resultsCount = Physics2D.OverlapCircleNonAlloc(transform.position, radius, results, itemMask);
for (int i = 0; i < resultsCount; i++) {
IItem item = results[i].GetComponent<IItem>();
if (item != null) {
MonoBehaviour m = item as MonoBehaviour;
if (m != null && m.enabled) {
item.OnPickup(this);
m.enabled = false;
GameObject.Destroy(m.gameObject);
}
}
}
}
public void OnDrawGizmosSelected() {
Gizmos.DrawWireSphere(transform.position, radius);
}
}
Thank you in advance for you help.
Regards!
For anyone who has a problem like that.
I fixed the problem. I had a .onMoneyChanged in an update function that was making the game crash.
I moved it to the onEnable and onDisable method and now it works just fine.
I have a Timer that counts down every 3 seconds (The white circle). It has a script attached called ReloadTimer.
I have a script that fires bullets (TankShooter) and reloads for 3 seconds.
How do I make it so that my countdown starts when I am reloading?
I tried looking at a lot of Unity forums and the advice didn't work.
ReloadTimer.cs
[ExecuteInEditMode]
public class ReloadTimer : MonoBehaviour
{
public Image filled;
public Text text;
public float maxValue = 3;
public float value = 0;
// UpdateReload is called once per frame
public void UpdateReload ()
{
value = Mathf.Clamp(value, 0, maxValue);
float amount = value / maxValue;
filled.fillAmount = amount;
text.text = value.ToString();
}
}
TankShooter
public int m_PlayerNumber = 1;
public Rigidbody m_Shell;
public Transform m_FireTransform;
public AudioSource m_ShootingAudio;
public AudioClip m_FireClip;
public float m_ShellVelocity = 100f;
private string m_FireButton;
public int maxAmmo = 5;
private int currentAmmo;
public float reloadTime = 2f;
private bool isReloading = false;
public ReloadTimer reloadTimer;
public class TankShootingT : NetworkBehaviour
{
public ReloadTimer reloadTimer;
private void Start()
{
if (!isLocalPlayer)
{
return;
}
currentAmmo = maxAmmo;
m_FireButton = "Fire" + m_PlayerNumber;
}
private void Update()
{
if (isReloading)
return;
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
reloadTimer.UpdateReload();
if (m_FireButton == "Fire1" && Input.GetButtonUp(m_FireButton))
{
// we released the button, have not fired yet
CmdShoot();
}
}
IEnumerator Reload()
{
isReloading = true;
Debug.Log("Reloading...");
yield return new WaitForSeconds(reloadTime);
currentAmmo = maxAmmo;
isReloading = false;
}
[Command]
private void CmdShoot()
{
currentAmmo--;
// Instantiate and launch the shell.
Rigidbody shellInstance = Instantiate(m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;
shellInstance.velocity = m_ShellVelocity * m_FireTransform.forward;
// Server spawns the shell
NetworkServer.Spawn(shellInstance.gameObject);
m_ShootingAudio.clip = m_FireClip;
m_ShootingAudio.Play();
}
}
For starters, There isn't such thing as UpdateReload that would be "called once per frame" as this is not a predetermined Unity function, it is just a function that you created (You can read about this here). Another problem is that you didn't even call that function anywhere else in your scripts. And even if you did, Mathf.Clamp() needs to be placed in an Update() function so it can update it's value each frame.
I made some modifications to the scripts that you posted, but I haven't tested them yet. give it a try and let me know how it goes:
ReloadTimer.cs
public class ReloadTimer : MonoBehaviour
{
public static ReloadTimer Instance { set; get; }
public Image filled;
public Text text;
public float coolDownTime = 3;
public bool isCoolingDown = false;
void Awake()
{
Instance = this;
}
void Update()
{
if (isCoolingDown == true)
{
filled.fillAmount += 1.0f / coolDownTime * Time.deltaTime;
int percentageInt = Mathf.RoundToInt((filled.fillAmount / coolDownTime) * 10);
text.text = percentageInt.ToString();
}
}
}
TankShootingT.cs
public int m_PlayerNumber = 1;
public Rigidbody m_Shell;
public Transform m_FireTransform;
public AudioSource m_ShootingAudio;
public AudioClip m_FireClip;
public float m_ShellVelocity = 100f;
private string m_FireButton;
public int maxAmmo = 5;
private int currentAmmo;
public float reloadTime = 2f;
private bool isReloading = false;
public ReloadTimer reloadTimer;
public class TankShootingT : NetworkBehaviour
{
public ReloadTimer reloadTimer;
private void Start()
{
if (!isLocalPlayer)
{
return;
}
currentAmmo = maxAmmo;
m_FireButton = "Fire" + m_PlayerNumber;
}
private void Update()
{
if (isReloading)
return;
if (currentAmmo <= 0)
{
StartCoroutine(Reload());
return;
}
reloadTimer.UpdateReload();
if (m_FireButton == "Fire1" && Input.GetButtonUp(m_FireButton))
{
// we released the button, have not fired yet
CmdShoot();
}
}
IEnumerator Reload()
{
isReloading = true;
ReloadTimer.Instance.isCoolingDown = true;
Debug.Log("Reloading...");
yield return new WaitForSeconds(reloadTime);
currentAmmo = maxAmmo;
isReloading = false;
ReloadTimer.Instance.isCoolingDown = false;
ReloadTimer.Instance.filled.fillAmount = 0.0f;
}
[Command]
private void CmdShoot()
{
currentAmmo--;
// Instantiate and launch the shell.
Rigidbody shellInstance = Instantiate(m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;
shellInstance.velocity = m_ShellVelocity * m_FireTransform.forward;
// Server spawns the shell
NetworkServer.Spawn(shellInstance.gameObject);
m_ShootingAudio.clip = m_FireClip;
m_ShootingAudio.Play();
}
}
Hope this helps a bit.
Just want to know if the get property code in this script(on the end) is a shorthand code, which says, if the shootCooldown is less than or equal to 0 then return true?
if it is, then is this another way of writing an if condition?For me, the code seems to be returning a float value and not a boolean.
the whole code is posted below:
public class WeaponScript : MonoBehaviour
{
public Transform shotPrefab;
public float shootingRate = 0.25f;
private float shootCooldown;
void Start()
{
shootCooldown = 0f;
}
void Update()
{
if (shootCooldown > 0)
{
shootCooldown -= Time.deltaTime;
}
}
public void Attack(bool isEnemy)
{
if (CanAttack)
{
shootCooldown = shootingRate;
// Create a new shot
var shotTransform = Instantiate(shotPrefab) as Transform;
// Assign position
shotTransform.position = transform.position;
// The is enemy property
ShotScript shot = shotTransform.gameObject.GetComponent<ShotScript>();
if (shot != null)
{
shot.isEnemyShot = isEnemy;
}
// Make the weapon shot always towards it
MoveScript move = shotTransform.gameObject.GetComponent<MoveScript>();
if (move != null)
{
move.direction = this.transform.right; // towards in 2D space is the right of the sprite
}
}
}
public bool CanAttack
{
get
{
return shootCooldown <= 0f;
}
}
}
The code of the property does not return the value of
shootCooldown
But the result of the expression
shootCooldown <= 0f;
which is resolved to boolean.
It is equivalent to writing
if (shootCooldown <= 0f)
{
return true;
}
else
{
return false;
}