Method ignores class scope variable value change - c#

The issue is with the boolean isDodging. It is set to true in the Dodge() method. That should trigger an if statement in the Movement() method (called in FixedUpdate()) but that block is always skipped over. I'm attaching all of the class' code, because there must be something I'm missing here:
using UnityEngine;
public class MovementController
{
/* COMPONENTS */
public Rigidbody2D Rigidbody { private get; set; }
/* VARIABLES */
private bool isDodging = false;
private Vector2 dodgeDirection = Vector2.right;
private float dodgeDuration = 1f;
private float dodgeSpeed = 20f;
private float timer = 0f;
/* METHODS */
// Called in fixed update (since it's dealing with physics)
public void Movement(Vector2 currentPosition, Vector2 velocity)
{
Debug.Log("In movement: " + isDodging);
if (isDodging)
{
Debug.Log("Dodge 3");
Move(currentPosition, dodgeDirection * dodgeSpeed);
timer += Time.fixedDeltaTime;
if (timer >= dodgeDuration)
{
Debug.Log("Stopped dodging " + Time.fixedTime);
isDodging = false;
}
}
else
{
Move(currentPosition, velocity);
}
}
private void Move(Vector2 currentPosition, Vector2 velocity)
{
if (Rigidbody == null)
{
Debug.LogWarning("No rigidbody to move!");
return;
}
Rigidbody.MovePosition(currentPosition + (velocity * Time.fixedDeltaTime));
}
// Must be called while Movement is being called
public void Dodge(Vector2 direction, float maxSpeed, float speedMultiplier = 2f, float duration = 1f)
{
if (direction == Vector2.zero) { return; }
Debug.Log("Dodge 1 " + isDodging);
dodgeDirection = direction;
dodgeDuration = duration;
dodgeSpeed = maxSpeed * speedMultiplier;
isDodging = true;
Debug.Log("Dodge 2" + isDodging + Time.fixedTime);
timer = 0f;
}
}
The thing is, the "In movement: " log always shows isDodging as false, and the if block under it never runs. Meanwhile, "Dodge 2" will show true (as isDodging is changed right above it). And the weirdest: "Dodge 1" shows false the first time Dodge() is called, but true everytime its called after that - as if isDodging was changed to true in the class scope, and Movement() doesn't recognize that for some reason.
Both this functions are called in a separate MonoBehaviour:
public class CreatureMovement : MonoBehaviour
{
[Header("Movement")]
[SerializeField] protected Vector2Reference moveDirection;
[SerializeField] protected FloatReference maxSpeed;
[Header("Dodge")]
[SerializeField] private FloatReference dodgeDuration;
[SerializeField] private FloatReference dodgeSpeedMultiplier;
[Header("References")]
[SerializeField] private new Rigidbody2D rigidbody;
private readonly MovementController movement = new MovementController();
public float MaxSpeed { get => maxSpeed; }
private float speed;
private float Speed { get => speed; set => speed = Mathf.Clamp(value, 0, maxSpeed); }
public virtual Vector2 Velocity
{
get => moveDirection.Value * Speed;
set
{
moveDirection.SetValue(value.normalized);
Speed = value.magnitude;
}
}
private void Start() => movement.Rigidbody = rigidbody;
private void FixedUpdate() => movement.Movement(transform.position, Velocity);
public void Dodge() => movement.Dodge(moveDirection, maxSpeed, dodgeSpeedMultiplier, dodgeDuration);
}
Where Dodge() is called from player input.
Except for dodging, movement is ocurring exactly as expected. The problem probably isn't in the Move() method, as it doesn't have isDodging in it.
I have absolutey no idea why this is happening, the code seems so simple to me, but it just isn't working. Please help out with this.

You're calling Dodge on a prefab instead of the scene instance of that prefab which is running CreatureMovement.FixedUpdate.
I believe you can verify this by placing this code in Dodge (Source: Max-Pixel):
if (gameObject.scene.name == null) Debug.Log("It's a prefab!");
You need to change your input processing to call Dodge on the instance in the scene instead of a prefab.
You can do that by dragging the instance in the scene into the button onclick event, then selecting Dodge. Or, if you are spawning the object dynamically, you could, in Start, find a reference to the button, and add Dodge to its onClick listeners:
private void Start()
{
movement.Rigidbody = rigidbody;
// something along the lines of this...
Button buttonRef = GameObject.Find("ButtonName").GetComponent<Button>();
buttonRef.onClick.AddListener(Dodge);
}

Related

How do I lock the camera horizontal rotation if speed is 0 or less?

I'm working on a cars game and I'm trying to prevent the camera from moving horizontally if the model speed is null, I have tried many things, however I seem unable to do this because either when I get the camera to get locked it remains locked even if the object moves, and if not that it just keeps moving even with the object stopped, so I want to know how can I make that happen.
Code for cam Class
public class Cam : MonoBehaviour
{
public float sensitivityHor = 9.0f;
public float sensitivityVert = 9.0f;
public float minimumVert = -45.0f;
public float maximumVert = 45.0f;
public enum rotationAxes
{
//Given aliases to X and Y cordinates
keyX = 1,
}
public rotationAxes axes = rotationAxes.keyX;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Movement mov = GetComponent<Movement>();
if (mov.speed > mov.speed && mov.speed > 0 && axes == rotationAxes.keyX)
{
//Movimiento en el eje X de la camara
transform.Rotate(0, Input.GetAxis("Horizontal") * sensitivityHor, 0);
}
}
}
code for Movement class
public class Movement : MonoBehaviour
{
private CharacterController _charController;
public float speed = 3.0f;
// Start is called before the first frame update
void Start()
{
_charController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
float deltaZ = Input.GetAxis("Vertical") * speed;
Vector3 movement = new Vector3(0, 0, deltaZ);
movement = Vector3.ClampMagnitude(movement, speed);
movement *= Time.deltaTime;
movement = transform.TransformDirection(movement);
_charController.Move(movement);
}
}
You never change the value of speed so what sense does it make to check it?
Rather store the actual speed you are going to apply to the object in this frame
public class Movement : MonoBehaviour
{
[Header("References")]
[SerializeField] private CharacterController _charController;
[Header("Settings")]
[SerializeField] private float speed = 3.0f;
[Header("Debug")]
[SerializeField] private float actualSpeed;
// public Read-only property
public float ActualSpeed => actualSpeed;
// Start is called before the first frame update
private void Awake()
{
if(!_charController) _charController = GetComponent<CharacterController>();
}
// Update is called once per frame
private void Update()
{
actualSpeed = Input.GetAxis("Vertical") * speed;
// It is cheaper to clamp a single float value then a vector
// since "GetAxis" retusn a value between -1 and 1
// it will anyway never be greater then "speed" so enough
// to clamp it downwards
actualSpeed = Mathf.Max(deltaZ, 0);
actualSpeed *= Time.deltaTime;
// This already uses the world space forward vector
_charController.Move(transfor.forward * actualSpeed);
}
}
And now you can rather check for this actualSpeed value
[RequireComponent(typeof(Movement))]
public class Cam : MonoBehaviour
{
[Header("References")]
// Better reference this already via the Inspector
[SerializeField] private Movement mov;
[Header("Settings")]
[SerializeField] private float sensitivityHor = 9.0f;
[SerializeField] private float sensitivityVert = 9.0f;
[SerializeField] private float minimumVert = -45.0f;
[SerializeField] private float maximumVert = 45.0f;
[Space]
[SerializeField] private rotationAxes axes = rotationAxes.keyX;
public enum rotationAxes
{
//Given aliases to X and Y cordinates
keyX = 1,
}
private void Awake()
{
// as fallback get it ONCE
if(!mov) mov = GetComponent<Movement>();
}
// Update is called once per frame
private void Update()
{
if (mov.ActualSpeed > 0 && axes == rotationAxes.keyX)
{
transform.Rotate(0, Input.GetAxis("Horizontal") * sensitivityHor, 0);
}
}
}

Unity how to "power bounce" one object from another relative to it's angle?

I'm working on a simple 3D game where some balls (fixed Z position) fall along a path (using gravity and physics material) to a small flat platform and "power bounce" off this platform. The player can rotate this platform so I want to recreate a realistic bounce direction according to the platform's angle.
I'm new to coding but so far I've figured the relationship between the vector of the ball as it comes into collision with the platform and the platform's normal, which should be a perpendicular line from the surface and that can be used to reflect the ball's vector to the other direction.
I already used OnCollisionEnter and if statement to detect whether it's the platform you are colliding with, but I don't understand where to indicate the normal of the surface and how to access it. Should it be as a public class in the other object or can it be detected from the ball game object?
I tried some examples from this and other websites and got this far:
public class OnCollision : MonoBehaviour
{
public float speed = 25f;
public Rigidbody rb;
private Rigidbody rigid;
private void Start()
{
rigid = transform.GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision collision)
{
if (collision.transform.tag == "BouncePad") {
rb.velocity = transform.up * speed;
}
}
}
Now it bounces off vertically, so I'm guessing I should change the code where the transform.up * speed part is.
Could anyone guide me, please?
Much appreciated.
If you are already using Physics material, look into the Bounciness property. A value of 0 means no bounce, a value of 1 will lead to no loss of energy. The angle of the bounce will be calculated for you. Make sure you drag the physics material onto each object-- both the ball and the wall's material will have an effect.
Finally somebody gave me a hand and came to this solution:
public class Bounce : MonoBehaviour
{
public Rigidbody rb;
public float str = 0.21f;
public float str2 = 0.15f;
// Start is called before the first frame update
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void OnCollisionEnter(Collision col)
{
if (col.gameObject.tag == "BouncePad")
{
rb.AddForce(rb.velocity * str, ForceMode.Impulse);
}
if (col.gameObject.tag == "BouncePad2")
{
rb.AddForce(rb.velocity * str2, ForceMode.Impulse);
}
}
// Update is called once per frame
void Update()
{
}
}
public class BouncTest : MonoBehaviour
{
[SerializeField] private float hight = 3;
[SerializeField] private int times = 5;
[SerializeField] private float speed = 8;
private Vector3 _startPos;
private bool _checkUP;
private int _countTimes;
private float _hightbuf;
[HideInInspector]
public bool _bounceEnd;
private void Awake()
{
_startPos = transform.position;
}
public void TurnOnBounceEffect()
{
_bounceEnd = true;
_checkUP = false;
_hightbuf = hight;
_countTimes = 0;
}
private void FixedUpdate()
{
BounceEffect();
}
private void BounceEffect()
{
if (_bounceEnd)
{
if (!_checkUP)
{
if (transform.position.y <= (_startPos.y + _hightbuf))
transform.position = Vector2.MoveTowards(transform.position, new Vector2(_startPos.x, transform.position.y) + (Vector2.up * _hightbuf), speed * Time.fixedDeltaTime);
else
{
_checkUP = true;
}
}
else if (times != _countTimes)
{
if (transform.position.y > _startPos.y)
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
else
{
_countTimes++;
_checkUP = false;
_hightbuf /= 2;
}
}
else
{
transform.position = Vector2.MoveTowards(transform.position, _startPos, speed * Time.fixedDeltaTime);
if (transform.position.y <= _startPos.y)
{
_bounceEnd = false;
}
}
}
}
}

Move player on touch Unity C#

I created a simple script to move player on Keyboard input, though now I want to move player on touch input, how do I do this ?
Here's my code, so how do I edit this code to make it work ? I have jump working, but dunno how to do it for moving ?
using UnityEngine;
using System.Collections;
public class MoveGround : MonoBehaviour
{
public float y = 0f;
public Rigidbody2D rb;
//public float x = 0f;
//public float z = 0f;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
//move function
if (Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector2(0, y);
}
if (!Input.GetKey(KeyCode.W))
{
rb.velocity = new Vector2(0, 0);
}
if (Input.GetKey(KeyCode.S))
{
rb.velocity = new Vector2(0, -y);
}
//move function end
}
public void Move()
{
}
}
How do you want to move your character using touch input instead of keyboard input? Your current code moves the character depending on whether the "W" or "S" keys are being pushed.
You could make two UI buttons that correspond to "forward" and "backward" keys.
Add an Event Trigger component to those buttons. Add two event types to the buttons: "Pointer Down" and "Pointer Up."
Add a function to the list of each of those events.
Put this code onto an object in your scene.
bool movingForward;
bool movingBackward;
public float speed = 0f;// set this either here in code or in the editor
public Rigidbody2D rb;
void Start (){
rb = GetComponent<Rigidbody2D>();
movingForward = false;
movingBackward = false;
}
// Your forward button will call this function
public void moveForward (){
movingForward = true;
movingBackward = false;
}
// Your backwardbutton will call this function
public void moveBackward (){
movingForward = false;
movingBackward = true;
}
//
public void stopMoving(){
movingForward = false;
movingBackward = false;
}
void Update () {
if(movingForward || movingBackward ){// we're moving
if(movingForward ){// forward
rb.velocity = new Vector2(0, speed);
}else if(movingBackward ){// backward
rb.velocity = new Vector2(0, -speed);
}
}else{// we're moving neither forward nor backward
rb.velocity = new Vector2(0, 0);// so stand still
}
}
Point each function to that object. In the Pointer Down event, choose the "moveForward" function for the button you want to move your character forward, and the "moveBackward" function for the other button.
In the Pointer Up event for both, just choose the "stopMoving" function.
It's really simplistic, but it will work.
I agree with #PolakięGames. The best way to do it is creating two UI buttons with EventTriggers attached and assigning the methods MoveForward and MoveBackwards to PointerDown and StopMoving to PointerDown. Although I would implement it differently:
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class MoveExample : MonoBehaviour {
public float Acceleration = 4f;
public float Speed = 4f;
private Vector2 _velocity = Vector2.zero;
private Rigidbody _rigidbody;
private void Start() {
_rigidbody = GetComponent<Rigidbody>();
}
// movement methods
public void Move(float velocity) { _velocity.x = velocity * Speed; }
public void MoveForward() { Move(1f); }
public void MoveBackwards() { Move(-1f); }
public void StopMoving() { Move(0f); }
private void Update() {
_rigidbody.velocity = Vector2.Lerp(_rigidbody.velocity, _velocity, Time.deltaTime * Acceleration);
}
}
That way your object will move smootly and you can control the speed and acceleration. You have to change Rigidbody to Rigidbody2D if you intend to use it with 2D Physics.

Control animation and execute script when animation ends

My project is a military fps and i'm having some problems with animations.
I have 3 different weapons, 1 animator controller for each one and every weapon has a "enter" and "leave" animation. Like CS, COD, etc...
I need to know when my "leave" animation ends to disable the gameobject, enable the other one and play the "enter" animation.
I tryed to do this: http://answers.unity3d.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html but without sucess.
I'll leave here a print of the animator controller, the hierarchy and the script, if u need more details, just need to say.
Animator controller of the weapon number 1
All transitions to "Sair" (leave animation) have a trigger (AK47_sair) and the transition to "Extit" state have a trigger ("AK47_SairControlador")
On my code, when i press 2 (change to weapon number 2) i want to do the transition.
This is the hierarchy, my script is attached to "Jogador".
With my actual code, it disable tha AK47 gameobject when the leave animation still playing.
using UnityEngine;
using System.Collections;
public class FirstPerson : MonoBehaviour {
public float speed;
public float normalSpeed = 5.0f;
public float slowSpeed = 2.5f;
public float crchSpeed = 2.5f;
private Transform tr;
private float dist; // distance to ground
public float mouseSensitivity = 5.0f;
public float verticalRotation = 0.0f;
public float updownRange = 60.0f;
private float verticalSpeed = 0.0f;
public float jumpSpeed = 5.0f;
CharacterController player;
private GameObject AK47;
private GameObject Faca;
public float shootingRate = 0.15f;
public float shootCooldown;
private bool agachado = false;
public float camOriginalPositionY;
public float camCrouchPositionY;
private Animator controladorAnimacaoAK;
private Animator controladorAnimacaoFaca;
public CapsuleCollider playerCollider;
public Camera CameraPrincipal;
public int ArmaSelecionada;
public int UltimaArma;
void Start () {
player = GetComponent<CharacterController>();
shootCooldown = 0;
controladorAnimacaoAK = player.GetComponentInChildren<Animator>();
playerCollider = gameObject.GetComponent<CapsuleCollider> ();
CameraPrincipal = Camera.main;
ArmaSelecionada = 1;
AK47 = CameraPrincipal.transform.FindChild ("ak47_final_animado").gameObject;
}
void Update () {
if (Input.GetKeyDown (KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2)) {
UltimaArma = ArmaSelecionada;
ArmaSelecionada = 2;
if(UltimaArma == 1) {
controladorAnimacaoAK.SetTrigger("AK47_Sair");
controladorAnimacaoAK.SetTrigger("AK47_SairControlador");
AK47.SetActive (false);
}
}
if (Input.GetKeyDown (KeyCode.Alpha1)) {
UltimaArma = ArmaSelecionada;
ArmaSelecionada = 1;
// controladorAnimacaoAK.SetTrigger ("AK47_Entrar");
}
if (ArmaSelecionada == 1) {
// diz ao controlador da anim se o player esta a movimentar-se ou nao
controladorAnimacaoAK.SetFloat ("AK47_Deslocacao", player.velocity.magnitude);
//Debug.Log (player.velocity.magnitude);
// dispatar tiros
PlayerShoot PlayerShootScript = player.GetComponent<PlayerShoot> ();
if (shootCooldown > 0) {
shootCooldown -= Time.deltaTime;
}
if (Input.GetButton ("Fire1")) {
if (shootCooldown <= 0) {
shootCooldown = shootingRate;
PlayerShootScript.FireShoot ();
// animaçao
controladorAnimacaoAK.SetBool ("AK47_Disparar", true);
}
} else {
// animaçao
controladorAnimacaoAK.SetBool ("AK47_Disparar", false);
}
if (Input.GetKeyDown (KeyCode.R)) {
controladorAnimacaoAK.SetTrigger ("AK47_rec");
}
}
}
}
In Unity 5 you've got this new thing which is Animation State Machine Behavior : State Machine Behaviours
You can use it to specify behavior when the Animation Controller enters or leave specific states.
For exemple here I've got my door which have an Open and Close state, and let's say that I want to play a sound when the door is opening.
Here I clicked Opening, then Add Behaviour and set a random name for the test (Behavior Test in my case)
Then I just need to implement the function void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) to play a sound at the first frame the animation is running.
[SerializeField]
AudioClip open_sound;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
animator.GetComponent<AudioSource>().clip = open_sound;
animator.GetComponent<AudioSource>().Play();
}
In your case, you would want to implement a behavior in the state Disparar which implements the function OnStateExit([...]) and handle the weapon change.
To go a bit further I don't think you should handle the weapon change directly in the animation state, but maybe your script could send an event catched by a Game Controller that will actually handle the change of weapon.

Trying to launch a projectile towards a gameobject, doesn't move!=

I'm making a 2D Tower Defense game and want my towers to launch a prefab at minions. However it currently only spawns my desired prefab, but doesn't move it.
My two scripts:
public class Attacker : MonoBehaviour {
// Public variables
public GameObject ammoPrefab;
public float reloadTime;
public float projectileSpeed;
// Private variables
private Transform target;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider co){
if (co.gameObject.tag == "Enemy" || co.gameObject.tag == "BlockTower") {
Debug.Log("Enemy tag detected");
if(this.gameObject.tag == "Enemy" && co.gameObject.tag != "Enemy"){
Debug.Log("This is an Enemy");
// Insert for Enemey to attack Block Towers.
}
if(this.gameObject.tag == "Tower" && co.gameObject.tag != "BlockTower"){
Debug.Log("This is a Tower");
Tower Tower = GetComponent<Tower>();
Tower.CalculateCombatTime(reloadTime, projectileSpeed);
Transform SendThis = co.transform;
Tower.SetTarget(SendThis);
}
}
}
}
and
public class Tower : MonoBehaviour {
private Transform target;
private float fireSpeed;
private double nextFireTime;
private GameObject bullet;
private Attacker source;
// Use this for initialization
public virtual void Start () {
source = this.GetComponent<Attacker> ();
}
// Update is called once per frame
public virtual void Update () {
if (target) {
Debug.Log("I have a target");
//if(nextFireTime <= Time.deltaTime)
FireProjectile ();
}
}
public void CalculateCombatTime(float time, float speed){
Debug.Log("Calculate Combat Speed");
nextFireTime = Time.time + (time * .5);
fireSpeed = speed;
}
public void SetTarget(Transform position){
Debug.Log("Set Target");
target = position;
}
public void FireProjectile(){
Debug.Log("Shoot Projectile");
bullet = (GameObject)Instantiate (source.ammoPrefab, transform.position, source.ammoPrefab.transform.rotation);
float speed = fireSpeed * Time.deltaTime;
bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, speed);
}
}
Basicly Attacker detects the object that collides with it, then if its tag is Tower it will send the information to Tower. My debug shows that every function works, even "Debug.Log("Shoot Projectile");" shows up.
However it doesn't move towards my target so I guess "bullet.transform.position = Vector3.MoveTowards (bullet.transform.position, target.position, step);" is never being executed?
Vector3.MoveTowards only moves the object once, it's just a instant displacement when the FireProjectile is called.
You need to create some kind of projectile script with an Update() function to make it move over time.
Here is an example:
public class Projectile : MonoBehaviour
{
public Vector3 TargetPosition;
void Update()
{
transform.position = Vector3.MoveTowards(transform.position, TargetPosition, speed * Time.DeltaTime);
}
}
Then right after your bullet instantiation, set the target:
bullet.GetComponent<Projectile>().TargetPosition = target.position;
Hope it helps.
You have to update the position of the bullet. You are only moving when you create the bullet.
Try to make a list of bullets and use the update function to change the position.

Categories