Player randomly changing position after implementing UI elements - c#

So I've been following a tutorial on making a 2D game in Unity (I'm a complete newbie, this is my first contact with programming), and I wanted to add a feature to the game(bold).
The "heart system" that I added works correctly (the number of empty hearts is equal to damage taken by player), but it caused my player to transform his position in a wierd way. You can see, that there are boundries set (maxHeight =3,2, minHeight=-3,2), and the value of his movement as well (Yincrement = 3.2) and yet, after pressing up or down arrowkeys he seems to change Y position by around 4.67.
Here's the player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
}
}
}

Main Issue
The main problem is your for loop. It should only be used for updating the UI - not for multiple times calling your movement and Scene reload! You should close your for loop earlier.
for (int i = 0; i < hearts.Length; i++)
{
// you can also reduce these to single lines
hearts[i].enabled = i < numOfHearts;
if(i < numOfHearts) hearts[i].sprite = i < health ? heartFull : heartEmpty;
} // <-- ALREADY CLOSE IT HERE
if(health <= 0) ...
...
Position clamping
Your clamping of the position is extremely insecure! Imagine the current position being 3.1 which is still < maxHeight so you add the Yincrement once and it results in a maximum possible height of 6.3! That's not what you wanted.
You should clamp directly on the targetPosition not on the current transform.position. You could for example use Mathf.Clamp for making sure the targetPos.y always stays within the given range.
Also since both cases do something very similar I would reduce this to only one movement using a simply int variable for setting the direction:
...
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
var move = 0;
var targetPosY = targePos.y;
if (Input.GetKeyDown(KeyCode.UpArrow) && targetPosY < maxHeight)
{
move = 1;
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && targetPosY > minHeigth)
{
move = -1;
}
// Handle the movement according to the direction
// in one single code block for both directions
if(move != 0)
{
Instantiate(effect, transform.position, Quaternion.identity);
// increase/decrease the targetPosY according to the move direction
targetPosY += move * Yincrement;
// Now make sure it is within the range
targetPosY = Mathf.Clamp(targetPosY, minHeight, maxHeight);
// finally assign the new target position
targetPos = new Vector2(transform.position.x, targetPosY);
}

Could you please specify in detail what your game is about and what you are trying to do in each part of the script. I might be able to help you then. Also, if this is your first contact with programming, this is way to advanced. Start with something simpler and first understand the basic concepts of programming before moving on. Here is a good tutorial series to learn c# programming for absolute beginners.
https://www.youtube.com/watch?v=pSiIHe2uZ2w
I am not sure what you are trying to do but why is your movement control within your for loop. That might be why your are messing up. Try removing all of this code out of the for loop.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
Also this code should be after you check if the player has moved.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
Here is a tip to make movement a little easier. Use transform.translate instead of Vector2.MoveTowards. Transform.translate takes in 3 floats and changes you player's position by the three floats represented as a vector.
Here is an example
transform.translate(0,2,0);
This will change the players y position by 2. I thing your code should look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
transform.Translate(0,Yincrement,0)
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
transform.Translate(0,Yincrement,0);
}
}
}
After I receive your reply, I possibly could help but there are no guarantees because I only have about a years worth of experience too.

Related

Horizontal movement of player causes egregious numbers when checking vertical velocity

Player movement is working (at least somewhat) now, however one issue remains, and that's the insane numbers the y velocity of the Rigidbody2D on the player. Since the isGrounded check I plan to add will use velocity for the sake of stability, this needs to be fixed.
It confuses me, considering the velocity is 0 normally, but whenever moving left or right it changes to said high numbers.
Movement code:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerM : MonoBehaviour
{
private PControls control;
Rigidbody2D rb;
SpriteRenderer sp;
Transform tr;
public float speed = 0f;
public float speedC;
public float heightSpeed = 5f;
public float heightC;
public bool grounded;
void Start()
{
control = new PControls();
control.Enable();
rb = GetComponent<Rigidbody2D>();
sp = GetComponent<SpriteRenderer>();
tr = GetComponent<Transform>();
}
// Update is called once per frame
void FixedUpdate()
{
RaycastHit2D hit = Physics2D.Raycast(rb.position, -Vector2.up);
Color color = new Color(0, 0, 1.0f);
Debug.DrawRay(transform.position, Vector2.down);
speedC = rb.velocity.magnitude;
var pos = control.Movement.Move.ReadValue<float>();
float GetVerticalSpeed() => rb.velocity.y;
if(pos == -1)
{
sp.flipX = true;
}
else if(pos == 1)
{
sp.flipX = false;
}
if((pos == 0) && (speed > 0.1f))
{
speed -= 3f * Time.deltaTime;
}
else if(speed < 1.4f)
{
speed += Mathf.Abs(pos) * 8 * Time.deltaTime;
}
if(speedC < 7f)
{
rb.AddForce(new Vector3((pos * 5), 0f) * speed * Time.deltaTime, ForceMode2D.Impulse);
}
var jump = control.Movement.Jump.ReadValue<float>();
Debug.Log(GetVerticalSpeed());
Vector3 v = rb.velocity;
v.y = 10;
if(jump == 1)
{
rb.velocity = v;
}
}
}
Seems the main issue was fixed by converting the height velocity to an integer using System.Math.Round():
float vel = rb.velocity.y;
heightC = (int)System.Math.Round(vel);
Not sure it's the best solution, but it's something..

How can I add a slowdown effect when object is getting close to a target?

using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.AI;
public class Waypoints : UnityEngine.MonoBehaviour
{
public List<Transform> waypoints = new List<Transform>();
public float movementSpeed = 5.0f;
public float slowdownSpeed = 1f;
public float rotationSpeed = 2.0f;
public float waypointDistance = 0.1f;
public float slowdownDistance = 7f;
public bool moveBackward = false;
public bool moveLoop = false;
public bool includeTransformPosition = false;
private Transform targetWaypoint;
private int targetWaypointIndex = 0;
private int lastWaypointIndex;
private bool includeTransform = true;
private GameObject go;
// Use this for initialization
void Start()
{
go = new GameObject();
go.transform.position = transform.position;
if (moveBackward && waypoints.Count > 2)
{
lastWaypointIndex = 0;
targetWaypoint = waypoints[waypoints.Count - 1];
}
else
{
lastWaypointIndex = waypoints.Count - 1;
targetWaypoint = waypoints[targetWaypointIndex]; //Set the first target waypoint at the start so the enemy starts moving towards a waypoint
}
}
// Update is called once per frame
void Update()
{
if (includeTransformPosition && includeTransform)
{
waypoints.Insert(0,go.transform);
includeTransform = false;
}
else
{
if (includeTransformPosition == false)
{
waypoints.Remove(go.transform);
includeTransform = true;
}
}
float movementStep = movementSpeed * Time.deltaTime;
float rotationStep = rotationSpeed * Time.deltaTime;
Vector3 directionToTarget = targetWaypoint.position - transform.position;
Quaternion rotationToTarget = Quaternion.LookRotation(directionToTarget);
transform.rotation = Quaternion.Slerp(transform.rotation, rotationToTarget, rotationStep);
float distance = Vector3.Distance(transform.position, targetWaypoint.position);
CheckDistanceToWaypoint(distance);
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
transform.position = Vector3.MoveTowards(transform.position, targetWaypoint.position, movementStep);
}
void CheckDistanceToWaypoint(float currentDistance)
{
if (currentDistance <= waypointDistance)
{
targetWaypointIndex++;
UpdateTargetWaypoint();
}
}
void UpdateTargetWaypoint()
{
if (targetWaypointIndex > lastWaypointIndex)
{
targetWaypointIndex = 0;
}
targetWaypoint = waypoints[targetWaypointIndex];
}
}
At this part I'm trying to slowdown the movement speed but it's not changing the speed at all :
if(slowdownDistance < 7f)
{
movementSpeed -= movementSpeed * Time.deltaTime;
}
What I'm trying to do when the transform start to move increase the speed slowly to some constant speed and then when the transform is getting closer to the waypoint then if the distance is less then 7 decrease the speed down to 0 so the object will stop at the waypoint then after X seconds move back the transform to the transform original position(go.transform) with the same increasing decreasing speed movement.
but I can't even make the first simple slowdown.
you set slowdownDistance to 7, i dont see you ever reducing it below 7 but you have if statement that executes only it it is under 7 do you reduce it elsewhere?
You probably planned to compare slowdownDistance to some distance rather than comparing it to its own value.
If you want to slow down the whole scene you could use a:
while(distance < range)
{
Time.timeScale = 1 - (distance / value);
}
with this you can change the value and so when the player come close time will be slown down mostly.
With the same idea you can do:
while(distance < range)
{
speed = speedInitial - (distance / value);
}
If you want to change just the speed of the player

Can not play disabled audio source (Unity 2d)

I have a little problem; I'm trying to develop my own endless jumping game. That's why I created an enemy type that can fire small projectiles. Every time it shoots, a sound should be played, but it won't. Debug: "Can not play disabled audio source."
Originally that only didn't work out for the prefabs, but recently it didn't work out for the first, original opponent either. (I kinda messd up spmething in the code)
Every proposed solution would help me a lot :)
I know, there are some posts about this topic out there, but none of this has helped yet...
The kinda messy code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyingEnemy : MonoBehaviour {
public float startTimeBtwShots;
private float timeBtwShots;
private Vector3 spawnPoint;
public AudioSource shot;
public Animator animator;
private float flyingSpeed;
private float turningPoint;
private bool moving;
public GameObject projectile;
public GameObject player;
void Start ()
{
turningPoint = Mathf.Abs(transform.position.x);
flyingSpeed = 0.008f;
timeBtwShots = startTimeBtwShots;
moving = true;
shot.enabled = true;
}
void Update ()
{
//Ignore, moves enemy from left to right and back
if (moving)
{
transform.position = new Vector3(transform.position.x + flyingSpeed, transform.position.y, transform.position.z);
}
if (transform.position.x >= turningPoint || transform.position.x <= turningPoint * -1)
{
flyingSpeed *= -1;
transform.position = new Vector3(transform.position.x + flyingSpeed, transform.position.y, transform.position.z);
if (flyingSpeed <= 0) transform.eulerAngles = new Vector3(0, 180, 0);
else if (flyingSpeed >= 0) transform.eulerAngles = new Vector3(0, 0, 0);
}
//Important part: projectile gets spawn and a bit earlier the sound should be played...
if (timeBtwShots <= 0)
{
spawnPoint = transform.position;
spawnPoint.y -= 0.35f;
Instantiate(projectile, spawnPoint, Quaternion.identity);
timeBtwShots = startTimeBtwShots;
animator.SetBool("Shooting", false);
}
else
{
timeBtwShots -= Time.deltaTime;
if (timeBtwShots <= 0.3)
{
shot.volume = 1;
shot.Play();
}
if (timeBtwShots <= 0.6)
{
animator.SetBool("Shooting", true);
}
}
if (player.transform.position.y >= transform.position.y + 5.5f)
{
Destroy(gameObject);
}
}
}
Thank you in advance!!
Ego | Jakob
You should try on your Start function: shot.gameObject.SetActive(true);
It can be the problem.

Unity3D Rigidbody player jump is limited while running

I'm trying to programming movements of a main FPS character with a Rigidbody.
Camera and ZQSD displacements works well but jumping while moving is being very restricted :
Immobile jumping : Y (min) = 1; Y (max) = 2.7;
Mobile jumping : Y (min) = 1; Y (max) = 1.9;
It's very frustrating, especially for a platform game. Personnaly, I'd like to get same result for both actions.
Here's my C# code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FPSMovement : MonoBehaviour {
public float walkAcceleration = 150f;
public float maxWalkSpeed = 10f;
public float jumpVelocity = 500f;
public float maxSlope = 45f;
private Rigidbody rb;
private Vector2 horizontalMovement;
private bool isGrounded = false;
private bool doubleJumped = false;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody> ();
}
// Update is called once per frame
void Update () {
if(rb.velocity.magnitude > maxWalkSpeed){
rb.velocity = Vector3.ClampMagnitude(rb.velocity, maxWalkSpeed);
}
transform.rotation = Quaternion.Euler (0, GetComponentInChildren<FPSCamera>().currentYRotation, 0);
// Entrées clavier ZQSD déplaçant le joueur
float x = 0, y = 0, z = 0;
if (Input.GetKey (KeyCode.Z)) {
x += 1f;
}
if (Input.GetKey (KeyCode.S)) {
x -= 1f;
}
if (Input.GetKey (KeyCode.Q)) {
z -= 1f;
}
if (Input.GetKey (KeyCode.D)) {
z += 1f;
}
// Arrêt prompt du glissement
if ((!Input.GetKey (KeyCode.Z) && !Input.GetKey (KeyCode.S) && !Input.GetKey (KeyCode.Q) && !Input.GetKey (KeyCode.D))
&& isGrounded) {
rb.velocity /= 1.1f;
}
// Saut du joueur
if (Input.GetKey (KeyCode.Space) && isGrounded) {
rb.AddForce (0, jumpVelocity, 0);
// Deuxième saut
}
if (Input.GetKey (KeyCode.Space) && !doubleJumped) {
rb.AddForce (0, jumpVelocity, 0);
doubleJumped = true;
}
rb.AddRelativeForce (z * walkAcceleration, 0, x * walkAcceleration);
}
void OnCollisionStay(Collision other) {
foreach (ContactPoint contact in other.contacts) {
if (Vector3.Angle (contact.normal, Vector3.up) < maxSlope) {
isGrounded = true;
doubleJumped = false;
}
}
}
void OnCollisionExit() {
isGrounded = false;
}
}
I also have impression that player hovers when moving in the airs, but this is not the main question and above all just an impression.
A simple issue with few words to explain, but a real problem to resolve for this type of gaming !
May I ask you, Stackoverflow people to gently help me, a thousand thanks by advance mates.
PS: My native language is the french, so please, don't lose too much time on grammar or other english subtlety :)
It is because you clamp the total velocity of the player where you should only clamp the horizontal speed. If you walk at maximum speed and jump, your jump speed is added to the total velocity and your character should actually be travelling at a higher speed than the maximum walking speed. To fix it, you do this:
// Isolate the horizontal component
Vector3 horizontalVelocity = rb.velocity;
horizontalVelocity.y = 0;
if (horizontalVelocity.magnitude > maxWalkSpeed) {
// Clamp the horizontal component
Vector3 newVelocity = Vector3.ClampMagnitude(horizontalVelocity, maxWalkSpeed);
// Keep the original vertical velocity (jump speed)
newVelocity.y = rb.velocity.y;
rb.velocity = newVelocity;
}

Object gets stuck when trying to bounce off screen edges, am I doing this wrong?

public class AsteroidMovement : MonoBehaviour
{
public Vector2 speed;
public Vector2 direction;
private Vector2 movement;
private Vector3 TopScreenBound;
private Vector3 BottomScreenBound;
// Use this for initialization
void Start()
{
TopScreenBound = Camera.main.ViewportToWorldPoint(new Vector3(0f, 1f, 0f));
BottomScreenBound = Camera.main.ViewportToWorldPoint(new Vector3(0f, 0f, 0f));
}
// Update is called once per frame
void Update()
{
if (gameObject.transform.position.y >= TopScreenBound.y)
{
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
direction.y *= -1;
}
movement = new Vector2(speed.x * direction.x, speed.y * direction.y);
}
void FixedUpdate()
{
rigidbody2D.velocity = movement;
}
}
I am trying to have asteroids in my game bounce off the edge of my screen and I have got it working decently, but after a few bounces, the asteroid/object gets "stuck" in the wall and glitches out the playing area.
Am I going about this wrong? I cannot see where in the code its making the asteroids stuck after a couple of bounces.Thanks in advance :)
You must fix your object's position to bounce inside the screen, if your object is already outside of the screen and it does not fully enter in screen space in the next frame, then your object is changing it's direction infinitely until it enters or leaves the screen.
Change this:
if (gameObject.transform.position.y >= TopScreenBound.y)
{
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
direction.y *= -1;
}
To this:
if (gameObject.transform.position.y >= TopScreenBound.y)
{
gameObject.transform.position.y = TopScreenBound.y;
direction.y *= -1;
}
if (gameObject.transform.position.y <= BottomScreenBound.y)
{
gameObject.transform.position.y = BottomScreenBound.y;
direction.y *= -1;
}

Categories

Resources