Can't implement the command pattern at all in Unity game project - c#

I have been trying to implement a command pattern in my Tennis game unity project, I have tried this for a couple weeks to no avail. First I obviously have my original gamescript that connects to my Player gameobject (Character.cs), then I tried first by implementing am interface (Command.cs), then I created the keycommands (RightCommand.cs, LeftCommand.cs) that extend to said interface, then I created an invoker (invoker.cs) that store the keycommands and implements them, then I tried to use the ivoker and keycommands in my Character.cs class but when I ran it, nothing happened when trying to move left. Here's my code to get a better picture.
Command.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface Command
{
void Execute();
}
RightCommand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RightCommand : Command
{
Vector3 position;
float speed = 3f;
public RightCommand(Vector3 vector)
{
position = vector;
}
public void Execute()
{
position.x -= speed * Time.deltaTime;
}
}
LeftCommand.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LeftCommand : Command
{
Vector3 position;
float speed = 3f;
public LeftCommand(Vector3 vector)
{
position = vector;
}
public void Execute()
{
position.x += speed * Time.deltaTime;
}
}
Invoker
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Invoker
{
Stack<Command> mCommands;
public Invoker()
{
mCommands = new Stack<Command>();
}
public void Execute(Command command)
{
if (command != null)
{
mCommands.Push(command);
mCommands.Peek().Execute();
}
}
}
Character.cs - here I tried to implement the LeftCommand and invoker to see if it would work with the left arrow key, but it ended up not working for some reason.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Character : MonoBehaviour
{
public Leftmove left;
float speed = 3f;
float force = 10;
bool hit;
public Transform target;
public Transform Tennisball;
Animator animator;
private Invoker minvoker;
// Start is called before the first frame update
void Start()
{
minvoker = new Invoker();
animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
Vector3 position = transform.position;
Vector3 positiont = target.position;
if (Input.GetKeyDown(KeyCode.F))
{
hit = true;
}
else if (Input.GetKeyUp(KeyCode.F))
{
hit = false;
}
if (Input.GetKey(KeyCode.LeftArrow) && hit == false)
{
Command command = new LeftCommand(position);
minvoker.Execute(command);
}
if (Input.GetKey(KeyCode.RightArrow) && hit == false)
{
position.x -= speed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.UpArrow) && hit == false)
{
position.z -= speed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.DownArrow) && hit == false)
{
position.z += speed * Time.deltaTime;
}
if ((hit == true) && (Input.GetKey(KeyCode.LeftArrow)))
{
positiont.x += speed * Time.deltaTime;
}
if ((hit == true) && (Input.GetKey(KeyCode.RightArrow)))
{
positiont.x -= speed * Time.deltaTime;
}
if ((hit == true) && (Input.GetKey(KeyCode.UpArrow)))
{
positiont.z -= speed * Time.deltaTime;
}
if ((hit == true) && (Input.GetKey(KeyCode.DownArrow)))
{
positiont.z += speed * Time.deltaTime;
}
transform.position = position;
target.position = positiont;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Tennis ball"))
{
Vector3 dir = target.position - transform.position;
//Vector3 horizo = transform.position;
//horizo.y = speed * Time.deltaTime;
other.GetComponent<Rigidbody>().velocity = dir.normalized * force + new Vector3(0, 10, 0);
Vector3 side = Tennisball.position - transform.position;
if (side.x >= 0)
{
animator.Play("backhand play");
}
else
{
animator.Play("forehand play");
}
//animator.Play("forehand play");
}
}
}

The problem here is that you are changing only Vector3 struct, but don't change actual Trasnform, so you can do something like that
Transform targetTransform;
public RightCommand(Transform transform)
{
targetTransform = transform;
}
public void Execute()
{
var targetPosition = transform.position;
targetPosition.x -= speed * Time.deltaTime;
targetTransform.position = targetPosition;
}
And in Character.cs change call to
if (Input.GetKey(KeyCode.LeftArrow) && hit == false)
{
Command command = new LeftCommand(transform);
minvoker.Execute(command);
}

Related

Grenade spawns in the wrong location

In my game I want to have a floating monster that's attack throws a grenade at the player. My problem is that the grenade only spawns in 0, 0, 0. In my script I make it so that the zombies spawns in on its own location but for some reason that doesn't work. I tried making it spawn by having the spawn location equal new Vector3(100, 100, 100) but it still spawned at 0, 0, 0. I know that the co-routine runs because I put a Debug.Log. Thanks for the help!
Edit #2: I can't have a rigidbody on the script. I have edited the movement script and I have found that no mater what if a rigidbody is added then it will go to 0, 0, 0.
Edit #3: I updated the scripts
Here is my script: (Sorry if the code is bad)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZKAttack_lvl3 : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 2.0f;
public float InRadius = 10.0f;
public float AttackRange = 15.0f;
private Coroutine hasCourutineRunYet;
public GameObject grenade;
public GameObject FloatingMonster;
private Vector3 FloatingMon;
private Animator anim;
private Rigidbody rigid;
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
rigid = GetComponent<Rigidbody>();
}
void Update()
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
float dstSqr = (Player.position - transform.position).sqrMagnitude;
bool inRadius = (dstSqr <= InRadius * InRadius);
bool inAttackRange = (dstSqr <= AttackRange * AttackRange);
anim.SetBool("AttackingPlayer", inAttackRange);
if (inRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
}
rigid.AddForce(1, 10, 1);
if (inAttackRange)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeAttack());
}
}
}
IEnumerator GrenadeAttack()
{
FloatingMon = FloatingMonster.transform.position;
GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);
yield return new WaitForSeconds(2.0f);
}
}
Edit: This is the code for the movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeMovement : MonoBehaviour
{
public float speed = 10f;
public float lifeDuration = 4.0f;
private float lifeTimer;
private Coroutine hasCourutineRunYet;
private Transform Player;
public SphereCollider sphereCollider;
// Use this for initialization
void Start()
{
lifeTimer = lifeDuration;
sphereCollider.enabled = false;
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Update is called once per frame
void Update()
{
lifeTimer -= Time.deltaTime;
if (lifeTimer >= 3f)
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
transform.position += transform.forward * speed * Time.deltaTime;
}
if (lifeTimer >= 0f)
{
transform.position = speed * transform.up * Time.deltaTime;
transform.position = speed * transform.forward * Time.deltaTime;
}
if (lifeTimer <= 0f)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
private void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player")
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
IEnumerator GrenadeExplosion()
{
sphereCollider.enabled = true;
yield return new WaitForSeconds(1.0f);
Destroy(gameObject);
}
}
With the help of #ken I figured out that I couldn't use a rigidbody so I changed the insantiation to this: GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);. I then changed the movement script for it to:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GrenadeMovement : MonoBehaviour
{
public float speed = 10f;
public float lifeDuration = 4.0f;
private float lifeTimer;
private Coroutine hasCourutineRunYet;
private Transform Player;
public SphereCollider sphereCollider;
public Vector3 velocity;
// Use this for initialization
void Start()
{
lifeTimer = lifeDuration;
sphereCollider.enabled = false;
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Update is called once per frame
void Update()
{
lifeTimer -= Time.deltaTime;
if (lifeTimer >= 2f)
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player);
transform.position += transform.forward * 5 * Time.deltaTime;
}
if (lifeTimer >= 0f && lifeTimer <= 2f)
{
transform.position = 9.18f * transform.up * Time.deltaTime;
transform.position = 9.18f * transform.forward * Time.deltaTime;
}
if (lifeTimer <= 0f)
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
private void OnTriggerEnter(Collider coll)
{
if (coll.gameObject.tag == "Player" || coll.gameObject.tag == "Terrain")
{
if (hasCourutineRunYet == null)
{
hasCourutineRunYet = StartCoroutine(GrenadeExplosion());
}
}
}
IEnumerator GrenadeExplosion()
{
sphereCollider.enabled = true;
yield return new WaitForSeconds(1.0f);
Destroy(gameObject);
}
}
Thank you for all your help, I have been trying to fix this all week.
You could set its position in the Instantiate line. Instantiate has several arguments. You can set its position in Instantiate, as well as its rotation and parent.
Set it to this:
IEnumerator GrenadeAttack()
{
GameObject bulletObject = Instantiate(grenade, FloatingMonster.transform.position, Quaternion.identity);
yield return new WaitForSeconds(2.0f);
}

sprinting wont work unless I turn off maximize on play and select my player

I'm trying to make a 2d platformer and a really long time ago I added sprinting and it was working perfectly. yesterday it randomly stopped working and I have no idea why so I tried to test if my speed and current stamina values were changing when I held down shift by turning off maximize on play and selecting my player when holding down shift. when I tested it the values were changing and for some reason my sprint was working, but when I stopped selecting my player and selected something else, it stopped letting me sprint. I don't think this is a bug with my code I just think this is a unity glitch but I'll include my code anyways
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerContoller : MonoBehaviour
{
private Rigidbody2D rb;
public float speed = 7.34f;
public float jumpForce;
private float moveInput;
private bool isGrounded;
public Transform feetPos;
public float checkRadius;
public LayerMask whatIsGround;
public LayerMask Slow;
private float jumpTimeCounter;
public float jumpTime;
private bool isJumping;
public int maxStamina = 163;
public int currentStamina;
public bool canSprint;
public Stamina stamina;
private Animator anim;
void Start()
{
anim = GetComponent<Animator>();
rb = GetComponent<Rigidbody2D>();
currentStamina = maxStamina;
stamina.SetMaxStamina(maxStamina);
}
private void FixedUpdate()
{
moveInput = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(moveInput * speed, rb.velocity.y);
if(moveInput == 0)
{
anim.SetBool("isRunning", false);
} else
{
anim.SetBool("isRunning", true);
}
if (currentStamina > 0)
{
canSprint = true;
} else if (currentStamina < 0)
{
canSprint = false;
}
if (currentStamina < maxStamina)
{
RegenStamina(2);
}
if (canSprint == true)
{
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftShift))
{
LoseStamina(5);
speed = 14f;
}
} else
{
speed = 7.34f;
}
if (canSprint == false)
{
speed = 7.34f;
}
}
private void Update()
{
isGrounded = Physics2D.OverlapCircle(feetPos.position, checkRadius, whatIsGround);
if (moveInput > 0)
{
transform.eulerAngles = new Vector3(0, 0, 0);
}
else if (moveInput < 0)
{
transform.eulerAngles = new Vector3(0, 180, 0);
}
if (isGrounded == true && Input.GetKeyDown(KeyCode.Space))
{
anim.SetTrigger("takeOff");
isJumping = true;
jumpTimeCounter = jumpTime;
rb.velocity = Vector2.up * jumpForce;
}
if (isGrounded == true)
{
anim.SetBool("isJumping", false);
}
else
{
anim.SetBool("isJumping", true);
}
if(Input.GetKey(KeyCode.Space) && isJumping == true)
{
if(jumpTimeCounter > 0)
{
rb.velocity = Vector2.up * jumpForce;
jumpTimeCounter -= Time.deltaTime;
}
else
{
isJumping = false;
}
}
if (Input.GetKeyUp(KeyCode.Space))
{
isJumping = false;
}
}
void LoseStamina(int StaminaGone)
{
currentStamina -= StaminaGone;
stamina.SetStamina(currentStamina);
}
void RegenStamina(int AddStamina)
{
currentStamina += AddStamina;
stamina.SetStamina(currentStamina);
}
}
and in a different script I have
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Stamina : MonoBehaviour
{
public Slider slider;
public void SetMaxStamina(int Stamina)
{
slider.maxValue = Stamina;
slider.value = Stamina;
}
public void SetStamina(int Stamina)
{
slider.value = Stamina;
}
}
(this is to make the slider move)
Since your max stamina is 163 and you reduce 5 stamina every FixedUpdate it is very likely that you won't experience the sprinting effect for long enough to notice it (~0.4s) assuming you did not change the default fix update rate from 0.02 (50 updates per second).
You can try increasing the stamina value to test it out or reduce the stamina consumption every execution of the FixedUpdate method.
If you want to do it in FixedUpdate, try the following:
LoseStamina(5 * Time.fixedDeltaTime);
This will change the stamina consumption to 5 per second, you can tweak it as you see fit.

rigidbodycomponent.velocity.y = 0 apparently causes a bug (making pong)

I was testing out scripts and there was a bug, that output 0 as its Rigidbody.velocity.y only if it goes up (so when the random chance it goes down it doesn't break), until it becomes negative (collision with resitution 1 or mirror bouncy) which then output a nonzero integer, so working as normal.
Edit: The temporary solution is just to remove the if statement in if (rb.velocity.y != 0) but i wanna know why does it output 0
Edit2: Maybe its a Unity bug, and i am in Unity 2019.4, so updating might be the solution? So if the "bug" dissapears, im sorry for wasting your time
First code determines the ball, while the second determines the ai
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallMovement : MonoBehaviour
{
public float speed;
public Rigidbody2D rb;
public Vector3 startPosition;
public float rampSpeed;
private float startSpeed;
public float tempSpeed;
// Start is called before the first frame update
void Start()
{
startPosition = transform.position;
startSpeed = speed;
Launch();
}
// Update is called once per frame
void Update()
{
}
private void Launch()
{
float x = Random.Range(0,2) == 0 ? -1 : 1;
float y = Random.Range(0,2) == 0 ? -1 : 1;
rb.velocity = new Vector2(speed * x, speed * y);
}
private void OnCollisionEnter2D ()
{
tempSpeed = speed;
speed += rampSpeed;
rb.velocity = new Vector2(rb.velocity.x*speed/tempSpeed,rb.velocity.y*speed/tempSpeed);
GetComponent<AudioBall>().SoundCollision();
}
public void Reset()
{
rb.velocity = Vector2.zero;
transform.position = startPosition;
speed = startSpeed;
Launch();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public bool isPlayer1;
public float speed;
public float moveInput;
private Rigidbody2D rb;
public Vector3 startPosition;
//AI
[Header("Ball")]
public GameObject ball;
private Rigidbody2D rbBall;
private float sign;
public bool ActivateAI;
public bool TurnOnOveride;
// Start is called before the first frame update
void Start()
{
if (TurnOnOveride == false)
{
ActivateAI = PlayMenu.ActivateAI;
}
else
{
ActivateAI = true;
}
startPosition = transform.position;
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
if(ActivateAI == false || isPlayer1 == true)
{
//this part is just human control. dont mind this
if (isPlayer1 == true)
{
moveInput = Input.GetAxisRaw("Vertical");
}
else
{
moveInput = Input.GetAxisRaw("Vertical2");
}
}
//AI
else
{
rbBall = ball.GetComponent<Rigidbody2D>();
sign = Mathf.Sign(rbBall.velocity.y);
Debug.Log("sign:"+sign);
if (sign == -1)
{
moveInput = -1;
}
else
{
Debug.Log("rb.y:"+rb.velocity.y);
if (rb.velocity.y != 0)
{
moveInput = 1;
}
}
}
rb.velocity = new Vector2(rb.velocity.x, moveInput * speed);
}
public void Reset()
{
rb.velocity = Vector2.zero;
transform.position = startPosition;
}
}

I'm trying to lerp forward the player a fixed distance but it only works once. How do I allow the player to be moved more than once?

Please check the code. Once the player moves the fixed distance once, he doesn't move again even when the space input is given.
How Do I make it such that I can keep moving the player after the player has been moved once?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementPC : MonoBehaviour
{
[Header ("Move Settings")]
private Vector3 StartingPosition, EndingPosition;
public float moveDistance = 30f;
public float LerpTime = 1f;
private float CurrentLerpTime = 0;
public bool movePressed = false;
private void Start()
{
StartingPosition = transform.position;
EndingPosition = transform.position + Vector3.forward * moveDistance;
}
void Update()
{
if (Input.GetKeyDown("space"))
{
movePressed = true;
Debug.Log("dash pressed");
}
if (movePressed == true)
{
CurrentLerpTime += Time.unscaledDeltaTime;
if (CurrentLerpTime >= LerpTime)
{
CurrentLerpTime = LerpTime;
}
float LerpPercentage = CurrentLerpTime / LerpTime;
transform.position = Vector3.Lerp(StartingPosition, EndingPosition, LerpPercentage);
}
}
Any Help would be appreciated. Thanks for your time.
The issue is you are not resetting the variables that control the start/end positions of the movement. The Start() method is called when the object is added to the level, or Play is pressed and that is currently the only method that resets those values. Therefore if you want those values reset you need to determine when the movement has finished, something like:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovementPC : MonoBehaviour
{
[Header ("Move Settings")]
private Vector3 StartingPosition, EndingPosition;
public float moveDistance = 30f;
public float LerpTime = 1f;
private float CurrentLerpTime = 0;
private void Start()
{
ResetPositions();
}
void Update()
{
if (Input.GetKeyDown("space"))
{
CurrentLerpTime += Time.unscaledDeltaTime;
if (CurrentLerpTime >= LerpTime)
{
ResetPositions();
return;
}
float LerpPercentage = CurrentLerpTime / LerpTime;
transform.position = Vector3.Lerp(StartingPosition, EndingPosition, LerpPercentage);
}
}
void ResetPositions()
{
StartingPosition = transform.position;
EndingPosition = transform.position + Vector3.forward * moveDistance;
CurrentLerpTime = 0;
}

The type or namespace name 'Score' could not be found

I don't know why, but I see to be getting thrown this error.. I followed a tutorial and haven't missed anything so far but now I have come to this.
The type or namespace name 'Score' could not be found (are you missing a using directive or an assembly reference?)
PlayerMotor.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class PlayerMotor : MonoBehaviour
{
private CharacterController controller;
private Vector3 moveVector;
private float speed = 10.0f;
private float verticalVelocity = 0.0f;
private float gravity = 12.0f;
private float animationDuration = 3.0f;
private bool isDead = false;
void Start () {
controller = GetComponent<CharacterController>();
}
void Update () {
if (isDead)
return;
if(Time.time < animationDuration)
{
controller.Move(Vector3.forward * speed * Time.deltaTime);
return;
}
moveVector = Vector3.zero;
if(controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
}
// X - Left and Right
moveVector.x = Input.GetAxisRaw("Horizontal") * speed;
// Y - Up and Dow
moveVector.y = verticalVelocity;
// Z - Forward and Backward
moveVector.z = speed;
controller.Move (moveVector * Time.deltaTime);
}
public void SetSpeed(float modifier)
{
speed = 10.0f + modifier;
}
private void OnControllerColliderHit (ControllerColliderHit hit)
{
if (hit.point.z > transform.position.z + controller.radius)
Death();
}
private void Death()
{
isDead = true;
GetComponent<Score>().OnDeath();
}
}
Score.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Score : MonoBehaviour
{
private float score = 0.0f;
private int difficultyLevel = 1;
private int maxDifficultyLevel = 10;
private int scoreToNextLevel = 10;
private bool isDead = false;
public Text scoreText;
void Update () {
if (isDead)
return;
if (score >= scoreToNextLevel)
LevelUp();
score += Time.deltaTime * difficultyLevel;
scoreText.text = ((int)score).ToString ();
}
void LevelUp()
{
if (difficultyLevel == maxDifficultyLevel)
return;
scoreToNextLevel *= 2;
difficultyLevel++;
GetComponent<PlayerMotor>().SetSpeed (difficultyLevel);
Debug.Log (difficultyLevel);
}
public void OnDeath()
{
isDead = true;
}
}
I am guessing both the scripts you have mentioned here are not attached to the same game object.
A quick fix to this would be to create a variable for the score script in your PlayerMotor script and then drag the score script to the variable in the editor to set it.
or you could just
do
GameObject.Find("Player").GetComponent<Score>().OnDeath();
or maybe you destroy the gameobject of the player on death, in that case nothing will work.
I'm not evening joking, I closed everything, reopened it and it works.
I'm done

Categories