Animation OnTrigger Unity - c#

I'm trying to make a fly-pan camera of a room when my first person player goes inside it. I've already recorded the animation on the camera but I'm having trouble when I change between cameras. I have read a lot a research about it but I actually don't know what I'm doing wrong. I would appreciate any kind of help. Thanks!
void OnTriggerEnter(Collider other)
{
inTrigger = true;
}
void OnTriggerExit(Collider other)
{
inTrigger = false;
}
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
// Update is called once per frame
void Update()
{
if (inTrigger/*&& !anim.isPlaying*/)
{
anim.Play("AnimateCameraFirst");
}
if (anim.IsPlaying("AnimateCameraFirst"))
{
cam.gameObject.SetActive(true);
FPCam.gameObject.SetActive(false);
}
else
{
FPCam.GetComponent<Camera>().enabled = true;
cam.GetComponent<Camera>().enabled = false;
}
}

Using Update() is not necessary here. Following code should be enough:
first, change FPCam GameObject to Camera to avoid GetComponent calls:
public Camera FPCam; // you have to reassign camera reference through inspector.
Camera cam; // as usual
void Start()
{
anim = GetComponent<Animation>();
cam = GetComponent<Camera>();
}
void OnTriggerEnter(Collider other)
{
anim.Play("AnimateCameraFirst");
cam.enabled = true;
FPCam.enabled = false;
}
void OnTriggerExit(Collider other)
{
FPCam.enabled = true;
cam.enabled = false;
}
To Run some code when animation finishes you have to create animation event and register your method to it. Have a look at how to implement this.
you can create this method and register it to the event which fires right at the end of animation:
public void CameraFirstAnimation_Stopped()
{
FPCam.enabled = true;
cam.enabled = false;
}
Hope this helps.

Related

Show and hide animation for a hand

So . I work at a 2d game in unity and I have some problems with some mechanics and i really need some help from you guys. All I want to do is to make my character hand to stop and grab a object. When I press E the animation of the hand start and have a collider for each frame, in case that the hand collide with a closer object, the animation to stop at that frame. I have a week since I try to figure it out. If you want to help me we can do it on discord. I will put the codes here, maybe the reason that I can't to what I want to do is very clear and I dont see it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class showHide : MonoBehaviour
{
[SerializeField]
GameObject hand;
private Animator freeze;
public bool touch = false;
private bool ok = true;
// Start is called before the first frame update
void Start()
{
freeze = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && ok == true)
{
freeze.Play("Follower");
StartCoroutine(show());
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
StartCoroutine(colliderShow());
}
}
private IEnumerator show()
{
ok = false;
hand.SetActive(true);
yield return new WaitForSeconds(4f);
hand.SetActive(false);
ok = true;
}
private IEnumerator colliderShow()
{
touch = true;
print(touch);
yield return new WaitForSeconds(4f);
touch = false;
print(touch);
}
private void FreezeAniamtion()
{
freeze.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
freeze.speed = 1f;
}
}
This is the code that activate and deactivate my hand object , including the animation .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class facingScript : MonoBehaviour
{
private SpriteRenderer render;
private Camera mainCam;
private Vector3 mousePos;
private Animator aniHand;
public bool atinge;
void Start()
{
mainCam = Camera.main;
render = GetComponent<SpriteRenderer>();
aniHand = GetComponent<Animator>();
gameObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
mousePos = Input.mousePosition;
mousePos = mainCam.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, mainCam.transform.position.z - transform.position.z)
);
Vector3 rotation = mousePos - transform.position;
if (rotation.x > 0)
render.flipY = true;
else
render.flipY = false;
if (atinge == false)
aniHand.speed = 1f;
Debug.Log(aniHand.speed);
}
// Collision with objects
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
atinge = true;
aniHand.speed = 0f;
StartCoroutine(freezingTime());
}
}
private IEnumerator freezingTime()
{
yield return new WaitForSeconds(4f);
atinge = false;
}
// No collision with any object
private void FreezeAnimation()
{
aniHand.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
aniHand.speed = 1f;
}
}
This is the hand component script . When the hand don't collide with anything the object is deactivated . The FreezeAnimation() is a event at the last frame. I tryed to do a collider animation for the showHide component that will be exact with the hand animation collider for each frame to check if the hand collide in the showHide script and if it collide to have a 4 seconds of waiting with the hand at that position.
I really tryed my best but I really can't figure it out.

Everything breaks after using PlayOneShot

In a game I am working on, the player uses a cube to open doors. When the player interacts with the cube while near the door, the door slides open. I am now trying to make it so that while it slides open, it makes a sound as doors should but I kinda ran into some issues. When I tried to add in the sound for the door, it ignored the part where the door should open altogether.
Here is what I did:
I added an AudioSource to the Cube's child object, CORE because the Cube object already contains an AudioSource which will play at the same time as this sound and assigned it in my Cube's script...
public AudioSource Woosh;
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
Here's the interactive part of the script, it runs a check but for some reason, it is pretending CanBeKey is false...
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
Now here is the IEnumerator part of the script, PlayDaWoosh()
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
I am aware that the last code snippet is a bit messy but it was the best thing I can think of.
Here is the full script in case you are that curious.....
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CUBE_CORE : MonoBehaviour
{
public AudioSource Hm; // HM!!!
private bool HasPlayed = false;
public float PlayTimeOut = 0.0f;
public static bool CanBeKey = false;
public Animator anim;
public static bool HasPlayedFirstTime = false;
public static AudioSource Door;
public AudioSource Woosh;
// Start is called before the first frame update
void Start()
{
//Debug.Log("Script initialized!");
Hm = gameObject.GetComponent<AudioSource>();
anim = GameObject.Find("TheFirstDoor").GetComponent<Animator>();
//Door = GameObject.Find("TheFirstDoor").GetComponent<AudioSource>();
Woosh = GameObject.Find("CORE").GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
if(HasPlayed == true)
{
if (PlayTimeOut < 0.0f)
{
HasPlayed = false;
}
PlayTimeOut -= Time.smoothDeltaTime;
}
}
public void OnActivate()
{
if(HasPlayed == false) //Have I played? Has the Time out passed?
{
HasPlayedFirstTime = true;
Hm.Play();
HasPlayed = true;
PlayTimeOut = 30.0f;
if(CanBeKey == true)
{
anim.Play("Door_Open");
//Door.Play();
StartCoroutine(PlayDaWoosh());
Debug.Log("OPENING!");
}
}
}
private void OnTriggerEnter(Collider other)
{
if(other.gameObject.name == "SecondDoor")
{
anim = GameObject.Find("DoorSecond").GetComponent<Animator>();
}
if(other.gameObject.name == "ThirdDoor")
{
anim = GameObject.Find("TheThirdDoor").GetComponent<Animator>();
}
if(other.gameObject.name == "FourthDoor")
{
anim = GameObject.Find("TheFourthDoor").GetComponent<Animator>();
}
}
IEnumerator PlayDaWoosh()
{
Woosh.PlayOneShot(Woosh.clip);
yield return new WaitForSeconds(Woosh.clip.length);
}
}
Expected result: Door opening and sound playing
Actual result: Neither happening unless I remove anything that has to do with the Woosh
I apologize ahead of time if I wasn't specific enough or if the question was answered somewhere else. I am relatively new here.

Instantiate Object Only once on game start Unity 3D

I have a game with several levels, each level has 6 scenes, the game start directly without any menu scene, and when the player open the game he can continue from the last scene that he already reached.
I want to instantiate some elements only on game opening (like Best score, Tap to play etc...), I mean that they should be instantiated only once on the start of the game (on the level he reached).
I tried this code in GameManager but it instantiate the elements in every scene:
public GameObject PlayButton;
bool GameHasEnded = false;
public float RestartDelay = 2f;
public float NextLevelDelay = 5f;
public int level_index;
private static bool loaded = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
}
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
public void CompleteLevel()
{
Invoke("NextLevel", NextLevelDelay);
}
public void EndGame()
{
if (GameHasEnded == false)
{
GameHasEnded = true;
Invoke("Restart", RestartDelay);
}
}
void NextLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex +1);
level_index = SceneManager.GetActiveScene().buildIndex + 1;
PlayerPrefs.SetInt("Last_Level", level_index);
PlayerPrefs.Save();
}
void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().path);
}
You already have an if block there with the static flag loaded
Since you there load another scene you need a similar second flag e.g.
private static bool loadedPrefab = false;
private void Start()
{
if (!loaded)
{
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
// Return because you don't want to execute the rest yet
// but instead in the new loaded scene
return;
}
// The same way skip if the prefab was already loaded before
if(!loadedPrefab)
{
loadedPrefab = true;
GameObject canvas = GameObject.Find("Canvas");
GameObject play = Instantiate(PlayButton, canvas.transform.position, Quaternion.identity);
play.transform.SetParent(canvas.transform, false);
}
}

Player stuck on reactivation - not changing position to spawn

I'm writing a script controlling the enter and exit of a space ship in a 2D game.
The problem I'm running into is when exiting the ship, the player is reactivated, and you regain control, but it does not move the player to the empty gameobject I have attached to the ship, in a beginners attempt to move the player to the location of the ship, without ever destroying the player. The player simply spawns on its last known location prior to entering the ship, and I seem to be forced into that location, I can try to move, but its like I'm anchored to that position.
using UnityEngine;
using System.Collections;
public class SpaceShipActivator : MonoBehaviour
{
//Takeoff
public bool inRange;
public SpaceShipAtmosphereController SpaceShipAtmosphereController;
public PlayerController playerControl;
public GameObject Player;
public bool onBoard;
//End Takeoff
//Landing
public Transform Spawn;
public Transform landingGear2;
public Transform landingGear;
public LayerMask whatIsGround;
float groundRadius = 0.2f;
bool getIn;
bool landingGearStable;
bool landingGearStable2;
bool shipStable;
//EndLanding
Animator anim;
void Start()
{
anim = GetComponent<Animator>();
inRange = false;
SpaceShipAtmosphereController.enabled = false;
}
void Update()
{
AnimationUpdate();
//Launch
if (inRange && Input.GetButtonDown("Interact") && !onBoard)
{
LaunchShip();
}
//End launch
CheckLandingGear();
//Land
if(onBoard && Input.GetButtonDown("Roll"))
{
LandShip();
}
//End Land
}
//*********************************Functions**************************************
void LaunchShip()
{
anim.SetBool("Get In", true);
this.gameObject.tag = "Player";
SpaceShipAtmosphereController.enabled = true;
Player.tag = "InactivePlayer";
playerControl.enabled = false;
Player.SetActive(false);
onBoard = true;
}
void LandShip()
{
anim.SetBool("Get Out", true);
this.gameObject.tag = "InactivePlayer";
Player.tag = "Player";
playerControl.enabled = true;
SpaceShipAtmosphereController.enabled = false;
onBoard = false;
Player.SetActive(true);
Player.transform.position = Spawn.transform.position;
}
void CheckLandingGear()
{
//landingGear
landingGearStable = Physics2D.OverlapCircle(landingGear.position, groundRadius, whatIsGround);
landingGearStable2 = Physics2D.OverlapCircle(landingGear2.position, groundRadius, whatIsGround);
if (landingGearStable && landingGearStable2)
{
shipStable = true;
}
//End landingGear
}
void AnimationUpdate()
{
anim.SetBool("Get In", false);
anim.SetBool("Get Out", false);
}
//For launching ship
//IF player: inRange enter/exit boolean control.
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
//Debug.Log("houi p;gberf");
inRange = true;
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.CompareTag("Player"))
{
inRange = false;
}
}
//End IF player: inRange enter/exit boolean control.
}
On a side note, In that same script I've included landingGear, which is set to be two empty gameobjects that basically share Y and are spaced apart on X to check that the spaceship is not only grounded but level for the most part, so the animation doesn't look silly AND to check if the player can even get out. I know this isn't the question, but any input on it would be appreciated, but please don't feel obligated.
Thanks for the help

Camera shake on collision in unity 3d

http://answers.unity3d.com/questions/212189/camera-shake.html
I've followed the question's answer above to try and get a camera shake working for my first person camera. But I've tried to modify it so that the camera shakes from an invisible collision box.
So far my camera shake script looks like this;
public bool Shaking;
private float ShakeDecay;
private float ShakeIntensity;
private Vector3 OriginalPos;
private Quaternion OriginalRot;
void Start()
{
Shaking = false;
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
DoShake();
Debug.Log("The camera trigger has hit");
}
}
void Update ()
{
if(ShakeIntensity > 0)
{
transform.position = OriginalPos + Random.insideUnitSphere * ShakeIntensity;
transform.rotation = new Quaternion(OriginalRot.x + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.y + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.z + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f,
OriginalRot.w + Random.Range(-ShakeIntensity, ShakeIntensity)*.2f);
ShakeIntensity -= ShakeDecay;
}
else if (Shaking)
{
Shaking = false;
}
}
void OnGUI() {
if (GUI.Button(new Rect(10, 200, 50, 30), "Shake"))
DoShake();
//Debug.Log("Shake");
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}
I know the code works 100% via the gui button. This script is attached to the camera on the first person controller. An invisible collision box with the tag ShakeTrigger is in the game. However, the debug log doesn't get called at all and I'm unsure why.
If anyone needs any more information just let me know.
Thanks in advance :)
If the script is attached to your camera, then OnTriggerEnter is looking at the camera for a trigger call, not the collision box.
One thing you could do is stick the OnTriggerEnter into a new script and put that inside the collision box. Then have that do a SendMessage along these lines:
GameObject.Find("Camera").SendMessage("DoShake");
EDIT: To answer Jerdak's questions.
The code bellow would be within the TriggerBox:
void Start()
{
...
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.name == "ShakeTrigger")
{
GameObject.Find("Camera").SendMessage("DoShake");
Debug.Log("The camera trigger has hit");
}
}...
and this would be within the Camera:
void Start()
{
...
}
public void DoShake()
{
OriginalPos = transform.position;
OriginalRot = transform.rotation;
ShakeIntensity = 0.3f;
ShakeDecay = 0.02f;
Shaking = true;
}...
This way, triggerbox is responsible for detecting triggers and only ever sends a message to the camera when right kind of object goes through it. The camera is then responsible for doing the shaking.
To shake your camera on the collision or Trigger you need to first Make function of Your Shake that you can also call from other scripts
Something like
public class ShakeCamera : MonoBehavior
{
public bool canShake;
private void Update()
{
if(canShake)
DoShake()
}
public void DoShake()
{
// Shake Logic
}
public void StartShake()
{
canShake = true;
}
public void StopShake()
{
canShake = false;
}
}
And from your other script whenever you trigger the target object you can call it like this
public class TriggerScript: MonoBehavior
{
public ShakeCamera shakeCamera;
private void Start()
{
shakeCamera = FindObjectOfType<ShakeCamera>();
}
void OnTriggerEnter(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StartShake();
}
}
void OnTriggerExit(Collider collision)
{
if(collision.gameObject.tag == "targetTag")// Change Tag accroding to your requirement
{
cameraShake.StopShake();
}
}
}
I am attaching one reference video for you, May be it would help you in better camera shake. Hope it was helpful.
You can refer to this video I made https://youtu.be/9X_JXexwfR4
If you have set up rigidbody then change the interpolate from none to interpolate.

Categories