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

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;
}

Related

Unity Rotate Object to Change Direction

I have a fish which is swimming across the screen. When it gets within 10% of the edge of the screen, I want it to turn begin turning around until it has completely reversed and is now swimming in the opposite direction. This should be more gradual like a fish would swim. I don't want it to rotate on its own axis.
I'm not having any luck getting it to turn around. It only turns partially.
Update
Here's the fish
public class FishSwim : MonoBehaviour
{
public enum Direction { LeftToRight, RightToLeft };
public Direction moveDirection;
[SerializeField]
private float speedMin = 0.5f;
[SerializeField]
private float speedMax = 1f;
[SerializeField]
private bool useOnlySpeedMax = false;
private float speed;
[HideInInspector]
public float removeBeyond;
private void Start()
{
var dist = (transform.position - Camera.main.transform.position).z;
if (moveDirection == Direction.RightToLeft)
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x;
else
removeBeyond = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, dist)).x + FindObjectOfType<SkinnedMeshRenderer>().bounds.size.x;
}
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
if (moveDirection == Direction.RightToLeft)
{
speed = -speed;
}
}
// Update is called once per frame
void Update()
{
float realSpeed = speed * Time.deltaTime;
transform.position += Vector3.right * realSpeed;
if (moveDirection == Direction.RightToLeft && transform.position.x < -Mathf.Abs(removeBeyond))
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(Vector3.right), Time.deltaTime * 40f);
moveDirection = Direction.LeftToRight;
}
else if (moveDirection == Direction.LeftToRight && transform.position.x > Mathf.Abs(removeBeyond))
{
}
}
}
I would use Coroutines to do this. Explanation in comments:
bool isTurning = false;
[SerializeField] float turnPeriod = 0.5f; // how long it takes to turn, smaller=faster
private void OnEnable()
{
speed = Random.Range(speedMin, speedMax);
if (useOnlySpeedMax)
{
speed = speedMax;
}
// Get rid of negative speed
}
void Update()
{
float realDistance = speed * Time.deltaTime;
transform.position += transform.right * realDistance;
// Surely there is a better way than calculating
// Mathf.Abs(removeBeyond) every frame... but that is off-topic
if (!isTurning && (
(moveDirection == Direction.RightToLeft
&& transform.position.x < -Mathf.Abs(removeBeyond)
) || (moveDirection == Direction.LeftToRight
&& transform.position.x > Mathf.Abs(removeBeyond)
) ) )
{
// If we aren't already turning and we should be, start turning:
StartCoroutine(Turn());
}
}
IEnumerator Turn()
{
isTurning = true;
Vector3 startForward = transform.forward;
// find end speed and direction
Direction endDirection = moveDirection == Direction.RightToLeft ?
Direction.LeftToRight:Direction.RightToLeft;
// keep track of how much of our turning time has elapsed
float elapsedTimePortion = Time.deltaTime/turnPeriod;
// turn until you've spent enough time turning
while (elapsedTimePortion < 1f)
{
// by whatever portion we've spent time turning, turn starting forward
// 180 degrees around up axis
float angle = 180f * elapsedTimePortion;
Vector3 newForward = Quaternion.AngleAxis(angle, Vector3.up) * startForward;
transform.rotation = Quaternion.LookRotation(newForward);
yield return null;
// next frame - update how long we've been turning
float newElapsedTimePortion = elapsedTimePortion + Time.deltaTime/turnPeriod;
if (newElapsedTimePortion >= 0.5f && elapsedTimePortion < 0.5f)
{
// If we've just passed 50% of the turn,
// make fish move opposite direction
moveDirection = endDirection;
}
elapsedTimePortion = newElapsedTimePortion;
}
// we're done turning, just set the rotation to the end rotation.
isTurning = false;
transform.rotation = Quaternion.LookRotation(-startForward);
// Does removeBeyond need to be updated here? There are a few lines in Start()
// which would suggest that.
}

How to fix Bullet Rotation in Unity? (2D)

So i'm having issues with a script I have, what it does is basically rotate the player graphics depending on where the mouse is aiming at. This works fine and as intended but my issue is that when I want to shoot on the opposite direction, that being the Left direction, it shoots the player instead of where it's aiming at.
I've decided to record a small video to show the problem.
https://streamable.com/02zqz
Here's the code to both the rotation and weapon script.
Rotating
public class Rotating : MonoBehaviour
{
public PlayerController player;
public float x;
public Vector3 flipArm;
void Start()
{
x = transform.localScale.x;
flipArm = transform.localScale;
player = GetComponentInParent<PlayerController>();
}
void Update()
{
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition ) - transform.position;
difference.Normalize();
float rotZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotZ + 0);
if(difference.x >= 0)
{
transform.rotation = Quaternion.Euler(0f, 0f, rotZ);
player.armRight = true;
}
else
{
transform.rotation = Quaternion.Euler(0f, 0f, rotZ+180);
player.armRight = false;
}
}
}
Weapon
public class Weapon : MonoBehaviour
{
public float shootDelay = 0;
public float damage = 1;
public LayerMask whatToHit;
public Transform firePoint;
public GameObject bullet;
// Start is called before the first frame update
void Start()
{
firePoint = transform.Find("FirePoint");
}
// Update is called once per frame
void Update()
{
shootDelay += Time.deltaTime;
if(shootDelay >= 0.1f)
{
if(Input.GetMouseButton(0))
{
shootDelay = 0;
Shot();
}
}
}
public void Shot()
{
Vector2 mousepos = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
Vector2 firepointpos = new Vector2(firePoint.position.x, firePoint.position.y);
Instantiate(bullet, firepointpos, transform.rotation);
Debug.DrawLine(firepointpos, (mousepos - firepointpos) * 100, Color.cyan);
}
}
Try this:
transform.rotation = Quaternion.LookRotation ( end - start );
Also don't forget to check where the player is facing because you don't want to shoot from back. Using the above euler angles, one side is 90 and another 270 degrees in y component.

Unity grounded state is flickering for character. .isGrounded being used but still flickering

The grounded state for my character controller flickers on and off constantly at what seems to be every frame. From what I know, it's supposed to check if the player is grounded through player.isGrounded, but something else is moving it back up.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerCharacterController: MonoBehaviour {
static Animator anim;
public bool walking;
public GameObject playerModel, Hero;
//Transforms
public Transform playerCam, character, centerPoint;
private Vector3 moveDirection;
//character controller declaration
CharacterController player;
//Mouse Rotation
private float rotX, rotY;
//Mouse Y Position
public float mouseYPosition = 1f;
//Mouse Sensitivity
public float Sensitivity = 10f;
//Mouse Zoom
private float zoom;
public float zoomSpeed = 2;
//Clamping Zoom
public float zoomMin = -2f;
public float zoomMax = -10f;
public float rotationSpeed = 5f;
//Move Front Back left & Right
private float moveFB, moveLR;
//Movement Speed
public float Speed = 2f;
//Velocity of Gravity
public float verticalVelocity;
//Jump Distance
public float jumpDist = 5f;
//Multiple Jumps
int jumpTimes;
//To use with Dialogue Manager
public DialogueManager DiagM;
public AudioClip jumpSound;
public AudioClip HurtSound;
public AudioClip PunchSound;
AudioSource audioSource;
//knockback
public float knockBackForce;
public float knockBackTime;
private float knockBackCounter;
// Use this for initialization
void Start ()
{
//character controller
player = GameObject.Find("Player").GetComponent<CharacterController> ();
StartCoroutine(MyCoroutine(character));
anim = GetComponent<Animator>();
//mouse zoom
zoom = -3;
centerPoint.transform.position = playerCam.transform.position;
centerPoint.transform.parent = null;
audioSource = GetComponent<AudioSource>();
}
IEnumerator MyCoroutine (Transform character)
{
if (player.isGrounded == true)
{
anim.SetBool("isFalling",false);
//anim.SetBool("isIdling", true);
yield return new WaitForSeconds(0);
}
}
// Update is called once per frame
void Update ()
{
//Mouse Zoom Input
zoom += Input.GetAxis ("Mouse ScrollWheel") * zoomSpeed;
if (zoom > zoomMin)
zoom = zoomMin;
if (zoom < zoomMax)
zoom = zoomMax;
//Mouse Camera Input
playerCam.transform.localPosition = new Vector3 (0, 0, zoom);
//Mouse Rotation
rotX += Input.GetAxis ("Mouse X") * Sensitivity;
rotY -= Input.GetAxis ("Mouse Y") * Sensitivity;
//Clamp Camera
rotY = Mathf.Clamp (rotY, -60f, 60f);
playerCam.LookAt (centerPoint);
centerPoint.localRotation = Quaternion.Euler (rotY, rotX, 0);
//Movement Speed
if (knockBackCounter <= 0)
{
moveDirection = (transform.forward * Input.GetAxis("Vertical")) + (transform.right * Input.GetAxis("Horizontal"));
moveDirection = moveDirection * Speed;
moveDirection.y = verticalVelocity;
player.Move(moveDirection * Time.deltaTime);
//Movement Rotation
centerPoint.position = new Vector3 (character.position.x, character.position.y + mouseYPosition, character.position.z);
//knockback disable
//Movement Input
if (Input.GetAxis("Vertical") != 0 || Input.GetAxis("Horizontal") != 0)
{
transform.rotation = Quaternion.Euler(0f, centerPoint.rotation.eulerAngles.y, 0f);
Quaternion turnAngle = Quaternion.LookRotation(new Vector3(moveDirection.x, 0f, moveDirection.z));
playerModel.transform.rotation = Quaternion.Slerp(playerModel.transform.rotation, turnAngle, Time.deltaTime * rotationSpeed);
if (player.isGrounded == true)
{
anim.SetBool("isWalking", true);
anim.Play("Running");
}
}
else
{
StartCoroutine(MyCoroutine(character));
}
if (Input.GetButtonDown("LHand"))
{
audioSource.PlayOneShot(PunchSound, 1F);
anim.Play("RPunch");
}
if (player.isGrounded == true)
{
jumpTimes = 0;
//verticalVelocity = -Physics.gravity.y * Time.deltaTime;
verticalVelocity = 0;
}
else
{
verticalVelocity += Physics.gravity.y * Time.deltaTime;
anim.SetBool("isWalking", false);
anim.SetBool("isFalling", true);
}
if (jumpTimes < 1)
{
if (Input.GetButtonDown("Jump"))
{
verticalVelocity += jumpDist;
anim.Play("Jump");
audioSource.PlayOneShot(jumpSound, 1F);
jumpTimes += 1;
}
}
}
else
{
knockBackCounter -= Time.deltaTime;
}
}
public void Knockback(Vector3 direction)
{
knockBackCounter = knockBackTime;
anim.Play("Jump");
audioSource.PlayOneShot(HurtSound, 50F);
moveDirection = direction * knockBackForce;
moveDirection.y = knockBackForce;
}
}
It looks like it has to do with the verticalVelocity lines, but so far I have only tried setting verticalVelocity = 0 and that works until I have actually moved the character. What could I change to stop the flickering?
Probably it is already solved, but the reason for that is that if you are using Character Controller you should apply gravity ALL the time to the character.
When the character collides with a object, it actually enters a little bit inside this object, then Unity pushes the character back away from the object, until it is no longer touching it. At this point, your gravity starts acting again, and re initiziling the cycle.
You need to apply gravity 100% of the time to create enough force to "balance" this fight with the floor. Could be a smaller "gravity" like 1. No need to be your gravity variable.
Also, on top of that, I like to add a "Coyote time", and make my on IsGrounded() method, as follows:
public bool IsGrounded()
{
return CoyoteTime < CoyoteTimeMax;
}
public void CoyoteControl()
{
if (CharController.isGrounded)
{
CoyoteTime = 0;
}
else
{
CoyoteTime += Time.deltaTime;
}
}
And then I call the CoyoteControl() on Update(), and I can call IsGrounded() whenever I need.
On the inspector I usually set the CoyoteTimeMax to 0.1 and it makes falls more smooth.
As per you comment. You should not determine if your player is grounded by checking an animation parameter. The best way is to use a RayCast(). So what you have to do:
Create a Layer named Ground, and add all the platforms in your
scene to that layer.
Create a bool variable
i.e
bool isGrounded;
Create a function to check if the character is grounded
Something like:
bool checkGrounded(){
return Physics.Raycast(transform.position, Vector3.down, 2f, 1 << LayerMask.NameToLayer("Ground")));
}
In this answer you can read about the involved parameters in the Raycast
Finally inside the update check if the player is grounded or not
Something like:
void Update(){
isGrounded = checkGrounded();
}
I have found that the isGrounded check can change over the course of the Update() function if you are checking it multiple times. Assigning it to a variable at the beginning of the function may solve the flickering issue.
void Update()
{
bool isGrounded = characterController.isGrounded;
...

How do I get the enemy character to stop circling the player?

I've been trying to figure this out for a few days now, and I'm very sure it has something to do with my code. What I want the enemy monster to do is to stop moving forward when the player right next to it and run the attack animation. What it actually does is run the attack animation while orbiting around the player like a planet, and you can't really outrun it.
I'm using Unity3d as the engine and my code is written in C#. It's very simple so I'm worried I'm missing something important.
public class enemyAITest : MonoBehaviour
{
public Transform player; // target
public float playerDistance; // determines how far from target before action takes place
public float rotationDamping; // determines how quickly rotation occurs
public float chaseSpeed; // determines how quickly to pursue target
public float wanderSpeed; // determines how quickly to move around map
public float maxDist;
static Animator anim;
Vector3 wayPoint;
void Awake()
{
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update ()
{
transform.position += transform.TransformDirection(Vector3.forward) * wanderSpeed * Time.deltaTime;
playerDistance = Vector3.Distance(player.position, transform.position);
while ((transform.position - wayPoint).magnitude < 3)
Wander();
if (playerDistance < 20f)
{
LookAtPlayer();
wanderSpeed = 0;
anim.SetBool("isIdle", true);
}
if (playerDistance < 10f)
{
Chase();
anim.SetBool("isIdle", false);
anim.SetBool("isWalking", false);
anim.SetBool("isRunning", true);
}
if(playerDistance < 1f)
{
wanderSpeed = 0;
anim.SetBool("isRunning", false);
anim.SetBool("isAttacking", true);
}
else anim.SetBool("isWalking", true);
}
void LookAtPlayer()
{
Quaternion rotation = Quaternion.LookRotation(player.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * rotationDamping);
wanderSpeed = 0;
}
void Chase()
{
Vector3 direction = player.position - transform.position;
direction.y = 0;
transform.Translate(Vector3.forward * chaseSpeed * Time.deltaTime);
}
void Wander()
{
Vector3 MonsterPosition = new Vector3(Random.Range(transform.position.x - maxDist, transform.position.x + maxDist), 1, Random.Range(transform.position.z - maxDist, transform.position.z + maxDist));
wayPoint.y = 1;
transform.LookAt(wayPoint);
//Debug.Log(wayPoint + " and " + (transform.position - wayPoint).magnitude);
}
}
Any advice will help (Please forgive the strange formatting, I was having trouble with keeping the entire Wander method and the on Awake line in the code block).

Why are my characters flipping through the floor?

I am working on a simple patrol script, and everything works but the characters are randomly rolling through the floor when they turn around. Here is a short clip of them...
Here is my script...
public class Patrol : MonoBehaviour
{
public float speed = 5;
public float directionChangeInterval = 1;
public float maxHeadingChange = 30;
public bool useRootMotion;
CharacterController controller;
float heading;
Vector3 targetRotation;
Vector3 forward
{
get { return transform.TransformDirection(Vector3.forward); }
}
void Awake()
{
controller = GetComponent<CharacterController>();
// Set random initial rotation
heading = Random.Range(0, 360);
transform.eulerAngles = new Vector3(0, heading, 0);
StartCoroutine(NewHeadingRoutine());
}
void Update()
{
transform.eulerAngles = Vector3.Slerp(transform.eulerAngles, targetRotation, Time.deltaTime * directionChangeInterval);
if (useRootMotion)
{
return;
}
else
{
controller.SimpleMove(forward * speed);
}
}
void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.gameObject.tag == "Player")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
if (hit.gameObject.tag == "Boundary")
{
// Bounce off the obstacle and change direction
var newDirection = Vector3.Reflect(forward, hit.normal);
transform.rotation = Quaternion.FromToRotation(Vector3.forward, newDirection);
heading = transform.eulerAngles.y;
NewHeading();
}
}
/// Finds a new direction to move towards.
void NewHeading()
{
var floor = transform.eulerAngles.y - maxHeadingChange;
var ceil = transform.eulerAngles.y + maxHeadingChange;
heading = Random.Range(floor, ceil);
targetRotation = new Vector3(0, heading, 0);
}
/// Repeatedly calculates a new direction to move towards.
IEnumerator NewHeadingRoutine()
{
while (true)
{
NewHeading();
yield return new WaitForSeconds(directionChangeInterval);
}
}
}
I have tried adding a rigidbody to the characters and constraining rotation, but that doesnt work. Oddly enough, the character control isnt rotating at all. In the scene view I can see the character collider staying as it should, but the character flips through the mesh on its own.
It looks like it's because they are walking into a corner and being bounced between the two walls constantly which causes them to behave strangely. I would add a method of checking for a series of very quick collisions to detect that they are in a corner or stuck and then adapt accordingly, perhaps with a method to rotate 180 degrees and keep walking or the like.
You can do it like this:
float fTime = 0.1f
float fTimer = 0;
int iCollisionCounter;
if(collision){
if(fTimer > 0) iCollisionCounter++;
if(iCollisionCounter >= 5) //Character is stuck
fTimer = fTime;
}
Void Update(){
fTimer -= time.deltaTime;
}
That means that if there are multiple collisions within 0.1 seconds of each other you can handle it.
Hope this helps!

Categories

Resources