Resetting Character Position in Unity is Not Working - unity3d

I'm working on a sports game where if a player skates into the goalie crease, I want all the player positions to reset to the center of the ice and do a three second countdown before play resumes.
I have tried to hardcode the starting position for the main player in a variable called PlayerStart and I call Player.transform.position = PlayerStart. When I did this, the player didn't move so I tried to switch the object I was setting as the player. This did what I wanted, but the mouse functionality changes for some reason and when the countdown ends, the player just goes right back to the position they were in before the crease violation was called.
Other things I've tried:
transform.SetPositionAndRotation
PlayerStart = Player.transform.position (instead of hard coding the numbers in)
Here is my code:
public class Crease : MonoBehaviour
{
private float Delay = 0;
private bool CreaseViolated = false;
private GameObject Player;
public Vector3 PlayerStart;
// Start is called before the first frame update
void Start()
{
Ring = GameObject.Find("Ring");
Player = GameObject.Find("FPSController");
PlayerStart = new Vector3(29.75f, 6.03999996f, 4.42000008f);
}
// Update is called once per frame
void Update()
{
Delay -= Time.deltaTime;
if (CreaseViolated)
{
CreaseViolation();
}
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.name != "Ring"
&& other.gameObject.name != "TeamGoalie"
&& other.gameObject.name != "OpponentGoalie")
{
CreaseViolated = true;
Delay = 3;
}
}
void CreaseViolation()
{
if (Delay > 0)
{
PlayerTip.GetComponent<PickupRing>().HasRing = false;
Opponent.GetComponent<AI>().HasRing = false;
Ring.transform.parent = null;
}
else
{
text.text = " ";
if (CreaseViolated)
{
Debug.Log("Player position before: " + Player.transform.position);
Player.transform.position = PlayerStart;
Debug.Log("Player position after: " + Player.transform.position);
//Player.transform.SetPositionAndRotation(PlayerStart + new Vector3(0f, 0.800000012f, 0f), new Quaternion(0f, -0.707106829f, 0f, 0.707106829f) + new Quaternion(0f, 0f, 0f, 1f));
GameObject.Find("Countdown").GetComponent<CountdownText>().timeRemaining = 4;
CreaseViolated = false;
}
}
}
}
Here is a short YouTube video showing my code and the demo: https://www.youtube.com/watch?v=mZt_4AppBh8

this problem is all solved now thanks to an awesome person at my university helping me out! The solution was disabling the CharacterController before repositioning the player and then enabling it again after.
So this:
Player.transform.position = PlayerStart;
in the CreaseViolation function becomes
cc.enabled = false;
PlayerController.transform.position = PlayerControllerStart;
cc.enabled = true;
with cc being declared earlier as
private CharacterController cc;
and in the start function I assigned it with the value
cc = Player.GetComponent<CharacterController>();
with PlayerController being set to the FPSController.
I renamed Player to PlayerController for more clarity.
Hopefully this helps anyone having the same problem I was having!

The Above answer works perfectly but might bring some issues when doing networked changes in player transform positions.
The reason the transform change doesn't work without disabling the Character Controller is because the character controller overrides the transforms.
To Disable this and enable an Auto Synchronization between transforms GoTo:
Edit > Project Settings > Physics > (Enable Auto Sync Transforms)

Related

Jump Animation Not Displaying in Unity 2D

I'm trying to "animate" (yes, I'm using the animate capabilities of Unity but I'm only changing the sprite from one still frame to another still frame) my character jumping in Unity. According to my code, everything should be running properly but for some reason, nothing happens in-game when I hit the Jump button (player is jumping but the sprite isn't changing). I'm following along with this tutorial and I've tried several others explaining how to use Unity's Animator and Animation windows but nothing seems to be working to change the sprite.
My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
[SerializeField]
private float moveForce = 10f;
[SerializeField]
private float jumpForce = 11f;
private float movementX;
private float movementY;
private Rigidbody2D myBody;
private SpriteRenderer sr;
private Animator anim;
private string WALK_ANIMATION = "Walk";
private bool isGrounded = true;
private string GROUND_TAG = "Ground";
private string JUMP_ANIMATION = "Jump";
private void Awake()
{
myBody = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
sr = GetComponent<SpriteRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
PlayerMoveKeyboard();
AnimatePlayer();
PlayerJump();
}
void FixedUpdate()
{
}
void PlayerMoveKeyboard()
{
movementX = Input.GetAxisRaw("Horizontal");
movementY = Input.GetAxisRaw("Vertical");
transform.position += new Vector3(movementX, 0f, 0f) * Time.deltaTime * moveForce;
}
void AnimatePlayer()
{
if (movementX > 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = false;
}
else if (movementX < 0)
{
anim.SetBool(WALK_ANIMATION, true);
sr.flipX = true;
}
else
{
anim.SetBool(WALK_ANIMATION, false);
}
}
void PlayerJump()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
isGrounded = false;
myBody.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
anim.SetBool(JUMP_ANIMATION, true);
Debug.Log("You should be jumping.");
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if(collision.gameObject.CompareTag(GROUND_TAG))
{
isGrounded = true;
anim.SetBool(JUMP_ANIMATION, false);
}
}
}
My animator states currently:
I'm not sure if this is enough information or if I've included screenshots of everything so please feel free to ask for more input.
I have tried:
Changing the Transitions from (currently) Walking to Jumping to (previously) Idle to Jumping.
Removing the Walking animation entirely from the code
Removing the Transitions from Walking to Idle so that there were only transitions from Walking to Jumping
Checking "Has Exit Time" and unchecking "Has Exit Time"
Extending the length of the Jumping animation
Changing the speed of the jump from 1 to .5 and from 1 to 2
Using an if/else statement in the Animate Player function where the bool of JUMPING_ANIMATION only becomes true if the Y value of the player is higher than the base position when they're standing on the ground
(Trying to; not sure if I coded this correctly because I'm shaky on using triggers instead of bools) Use a trigger instead of a bool for initializing the jump animation
I'm absolutely positive I've just missed something in the tutorial or something else fairly simple and dumb but I cannot for the life of me figure out what it is I'm missing. I've searched the other jump animation issues here on SO, too, and none of them seem to be quite what I'm missing. As it stands, my code is telling me that the Jump parameter is becoming true properly based on the Console Log but nothing about the visual sprite changes for the character.
As I can see in your Animator, you need to set your "StateMachine Default State" to one of your used states (I suppose for your case, the Idle State).
You can do that with a right click on the Entry State.

Difficulties flipping a collider in Unity when attacking

I am working on a small game for a school project, in which my player needs to attack enemies in a level. My plan is to have a collider that is enabled in an attached script, and then disabled when the attack is done. My current problem is that that the collider does not flip the way it is supposed to, it seems to flip directly on the overall x axis instead of flipping in the x axis related to the player. It is a child of the player so I am clueless as to why it is doing this. Any solutions or other approaches would be greatly appreciated. I will attach the current script that controls the collider below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VerticalSword : MonoBehaviour
{
//Variables use to active the colliders
Collider2D AttackColliderVertical;
//Variables for the location of the collider
Vector2 attackOffset;
private void Start()
{
AttackColliderVertical = GetComponent<Collider2D>();
attackOffset = transform.position;
AttackColliderVertical.enabled = false;
}
public void FixedUpdate()
{
attackOffset = transform.position;
}
public void AttackUp()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = attackOffset;
}
else if (attackOffset.y < 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1)); //I think the problem is somewhere in this if and else if statement
}
print("Attack up successful"); //Error checking (This works when run)
}
public void AttackDown()
{
AttackColliderVertical.enabled = true;
if (attackOffset.y > 0)
{
transform.position = new Vector2(attackOffset.x, (attackOffset.y * -1));
}
else if (attackOffset.y < 0)
{
transform.position = attackOffset; //I think the problem is somewhere in this if and else if statement
}
print("Attack down successful"); //Error checking (This works when run)
}
public void StopAttack()
{
AttackColliderVertical.enabled = false;
}
}
Use transform.localPosition, not transform.position (that's its world space position). You need to change it everywhere in this script; the Start() function and the two attack functions

AI returning to original position

I want my enemy to move back to starting position. He follows me until I get out of his range and then he just stops.
Also i want my skeleton to stop for like 5 sec, and then go back to starting point, any ideas ? I never did anything involving time, exept stopping it.
Here is my script for enemy:
Also here is a screenshoot of inspector on the skeleton: enemy
Here is my script for enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class chase : MonoBehaviour
{
public Transform player;
private Animator anim;
public float LookRadius = 15f;
public Transform enemyStartPos;
// Use this for initialization
void Start()
{
anim = GetComponent<Animator>();
this.enemyStartPos.position = this.transform.position;
}
// Update is called once per frame
void Update()
{
if (!PauseMenu.GameIsPaused)
{
if (Vector3.Distance(player.position, this.transform.position) < 15)
{
Vector3 direction = player.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 3)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
else
{
anim.SetBool("isAttacking", true);
anim.SetBool("isWalking", false);
}
}
else
{
if (Vector3.Distance(this.enemyStartPos.position, this.transform.position) >1)
{
Vector3 direction = this.enemyStartPos.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 1)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
}
else
anim.SetBool("isIdle", true);
anim.SetBool("isAttacking", false);
anim.SetBool("isWalking", false);
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, LookRadius);
}
}
Before you try to "code" this, think about it in terms of your program design. You have designed and implemented behavior that says "if the bot's position is within X units of the player's position, turn the bot and move it toward the player."
What you describe wanting to do next can be thought of in the same way (which should lead to similar code).. "else, if the bot's position is NOT within X units of the player's position, turn the bot and move it toward [origin]." Note, this means you need to define what [origin] is for the bot. Point being, it makes no difference in the code whether you are moving toward a player or some arbitrary fixed point. The code to move one transform toward another transform is the same.
For "wandering" it's essentially the same thought process: If bot is within X units of [origin] and not following player, pick a random direction and move that way. (better yet, pick a random direction and move that way for some amount of time so your bot doesn't just jitter around origin).

Field of View for enemy only works with 1 enemy

I have some problems with a script for a "Field Of View" of the enemy. After watching a tutorial to create a field of view for the player I thought I can switch it to the enemy's so that they detect the player and do some other stuff. I created a boolean variable playerInRange to detect if the enemies can detect the player and set this variable to true or false.
It works fine with just one enemy. When I add another one, the new enemy will not detect the player. So maybe it is related to the coroutine, but I am not sure.
Here is a little bit of my code:
void Start() {
StartCoroutine("FindTargetsWithDelay", .2 f);
}
IEnumerator FindTargetsWithDelay(float delay) {
while (true) {
yield
return new WaitForSeconds(delay);
FindVisibleTargets();
}
}
public void FindVisibleTargets() {
visibleTargets.Clear();
Collider[] targetsInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
for (int i = 0; i < targetsInViewRadius.Length; i++) {
Transform target = targetsInViewRadius[i].transform;
Vector3 dirToTarget = (target.position - transform.position).normalized;
if (Vector3.Angle(transform.forward, dirToTarget) < viewAngle / 2) {
float dstToTarget = Vector3.Distance(transform.position, target.position);
if (!Physics.Raycast(transform.position, dirToTarget, dstToTarget, obstacleMask)) {
// Not so nice solution!
// The movement should be in a separate script!
visibleTargets.Add(target);
nav.SetDestination(player.position);
anim.SetBool("IsRunning", true);
if (dstToTarget < attackRange) {
playerInRange = true;
Debug.Log(playerInRange);
}
}
} else {
anim.SetBool("IsRunning", false);
playerInRange = false;
Debug.Log(playerInRange);
}
}
}
Thank you guys for your little hint. It was really a little hierarchy problem :( Sorry for that newbie/DAU question.
Cheers
Nico

Unity3D scene not working properly and lagging after reloading

I have looked all over internet, but I cannot find a solution to my problem. I am trying to create a game and it has 3 scenes: the Game Start, Main and Game over.
The problem is that when trying to load the main level from other scenes it does not do what it has to do (e.g. jump) and the scene itself is lagging. When I try to load only the Main Scene it works ok, but after the character dies and the scene is reloaded, it starts to lag again and I cannot jump or do anything it is supposed to do.
Any ideas on what the problem might be?
using UnityEngine;
using System;
public class Player : MonoBehaviour
{
// The force which is added when the player jumps
// This can be changed in the Inspector window
public Vector2 jumpForce = new Vector2(0, 300);
private bool shouldJump = true;
// Update is called once per frame
private float jumpThreshold = 0.5f;
private float previousJumpTime;
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
GetComponent<Rigidbody2D>().velocity = Vector2.zero;
GetComponent<Rigidbody2D>().AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
// Die by collision
void OnCollisionEnter2D(Collision2D other)
{
Die();
}
void Die()
{
Application.LoadLevel ("main");
}
}
You said your level is called Main, but in the code you are loading "main", i'm not sure if that's the problem, but you seem to be loading the level correctly, so check if is main or Main the exact name of the level.
Also, when compiling, make sure you have all levels checked
With provided data its impossible to say what causes low performance, but I recommend you to use Profiler tool (can be found in personal version of Unity 5) and figure out what scripts and functions are problematic.
Also, try to avoid calling GetComponent<Rigidbody2D>() in Update/FixedUpdate/LateUpdate and cache this component instead.
I second what Utamaru said.
Rigidbody2D myRigidbody2d;
void Start ()
{
myRigidbody2d = GetComponent<Rigidbody2D>(); //Do this once
}
Inside your fixed update, you can do this:
void FixedUpdate ()
{
// Jump
float mc = MicControl.loudness;
//Debug.Log (mc);
if (mc>1.3f && shouldJump)
{
shouldJump = false;
previousJumpTime = Time.time;
myRigidbody2d.velocity = Vector2.zero;
myRigidbody2d.AddForce(jumpForce);
}
if (!shouldJump)
{
if(Time.time-previousJumpTime>jumpThreshold)
{
shouldJump = true;
}
}
// Die by being off screen
Vector2 screenPosition = Camera.main.WorldToScreenPoint(transform.position);
if (screenPosition.y > Screen.height || screenPosition.y < 0)
{
Die();
}
}
I can't see the rest of your code so I am not really sure that is the problem but give this a try.