I am new to C# and Unity and I'm trying to figure out how to make an animation for an object in unity play when I press a key but I can only make the animation play once, and then it is broken and doesn't work. (I am trying to make an FPS game)
The code I have right now looks like this:
void Start()
{
gameObject.GetComponent<Animator>().enabled = false;
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
gameObject.GetComponent<Animator>().enabled = true;
}
}
When I press run and left click, the animation triggers and does as it is supposed to but when I try to do it again, the animation doesn't work. Can anybody help me change this code so that the animation will work and play every time the button is pressed?
I am assuming your animation is non-looping as if it was looping it would already play back into itself when it is over.
One quick note I would have with your code is do not use GetComponent in an Update function as it is quite costly. An easy way to get an animation state to reset is to enable and disable it, however I am assuming you want to have more animations than shooting. You would want to look into what is called an Animation Tree or a Blend Tree and add States to your animation. Examples of states would be an Idle, Walk, Run, Shoot, Crouch, etc. I would consider researching Animation Trees and Blend Trees to get a full animation cycle in.
Once you get a State machine working, I would have the enter go to an Idle state, then either set a transition Bool or directly switch the animation in code.
// when you serialize a private field, it will appear in
// the inspector so you can drag in the reference in the editor
[SerializeField] private Animator anim = null;
private void Start()
{
anim.enabled = false;
}
private void Update
{
if(Input.GetButtonDown("Fire1")
{
Shoot();
if(!anim.enabled)
anim.enabled;
else
anim.Play("AnimationStateName", -1, 0f);
}
}
I have not tested the code, but I believe this would work with your setup. I would still strongly advise to not do this and look into Trees. After implementing the tree, instead of calling using the enabled, just use the line anim.Play("AnimationStateName", -1, 0f) or you can do anim.SetBool(isAttacking, true) if you set your state to transition from Idle/Run/Walk/etc. to Attacking when the isAttacking bool is set to true.
I found a video that might help you out. I do not want to post a full explanation to animation states and blend trees, just point you in the right direction to a better approach.
Related
I'm trying to make a mobile sidescroller and I want the player to be able to jump higher if the jump UI button is held down, I could not find any toturials on youtube for mobile and I'm reletively new to scripting so I tried to do it by myself but it did not work properly, the only toturials I can find are for pc and I can't implement them correctly for mobile because they require Input.getbuttondown and inputs like that impossible for mobile, this is my current script, the jump ui button has event trigger
public void Jump()
{
if (isGrounded())
{
jumping = true;
animator.SetBool("NowJumping", true);
SoundManager.PlaySound("PlayerJump");
rb.velocity = new Vector2(rb.velocity.x, jumpPower);
if (jumping)
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f);
}
}
public void StopJump()
{
jumping = false;
}
the jump is always boosted by the if (jumping) velocity boost,
I'm out of ideas on how to put this feature in my game, anyone have any ideas how to fix this? Thank you very much in advance
I would start coroutine when jumping starts. Like 0.5 secs long. After coroutine ends, it makes the jumping false and stops jumping even if you keep pushing the button down. I am in my phone, so i can't test this. I suppose while you are pressing the button down, it always makes the jumping true because of the way you designed your code. So to make it work, you can make jumping true when key is pressed the first time (not holding down). Then in OnButtonHeldDown() you can make if (jumping) {bla bla}. This way when coroutine ends it makes jumping false and jumping would not be true again while pressing button. Player needs to release and press again to make jumping true. And like you wrote, it cannot release button and press again the trick the code, because only when it is grounded it can jump. I hope i managed to explain my reasoning.
Also you can make the button uninteractible after 0.5 secs and make in interactible again after the character is grounded.
For coroutine usage, you can check here.
https://docs.unity3d.com/Manual/Coroutines.html
Set jumping to false using OnPointerUp. You can read how to use it here
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/UI.Selectable.OnPointerUp.html
I'm trying to make a game where you're a boy and you can transform into a deer.
I don't know how to make the player transform into a deer when pressing a button. Can someone please tell me how to make the player transform into a deer when pressing a button and how to make the deer transform back into the player when pressing the button again?
There are 2 ways depending on what you want exactly.
1.Transformation without an animation, you can do this by having 2 separate models
public GameObject boy;
public GameObject deer;
//Give both objects the same tag and don't add anything else under the
tag in order for this to work
private Transform currentPosition;
private void Start()
{
deer.SetActive(false);
}
public void TransformButton()
{
currentPosition = GameObject.FindObjectWithTag(LayerMask.NameToLayer("TransformationTag"));
//Unity doesn't pick up disabled GameObjects so it will pick up only the
//active state (the active object)
boy.SetActive(!activeInHierarchy);
deer.SetActive(!activeInHierarchy);
GameObject.FindObjectWithTag(LayerMask.NameToLayer("TransformationTag")).getComponent<Transform>() = currentPosition;
}
I recommend this way if your deer and boy have different scripts
you could also play an animation on click (apply this animation to the active character) and when it is done (below is how to check if it is done) you can trigger the code above
https://answers.unity.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html
I am pretty sure there are other better ways but this is the way I do it
The other way which is probably easier if you are good at animating is to just trigger an animation using the animation bools when the button is clicked but I haven't tried this way because I'm not that good at animation. Instead I use the first way (usually for switching skins in a shop for example) but I never did animations that completely change the size ratio so much so I don't know how well would it work (I usually do 1:1 transformations like humanoid to humanoid or tank to tank so you will have to experiment a bit)
I dont think that this is possible in unity. Maybe there are some Assets that offer something like this.
But you could create a similar effect with Blender.
Check out Morphing Shape Animations.
There are some cool tutorials out there Morphing Shape Animations in Blender
So I have an animation that fades into the menu screen but after the animation ends none of my buttons work. I have figured out that it is because the GameObject that holds the black image that fades to clear is always in the front, blocking me from using any of the buttons. I tried to write a script, that's attached to the game object, that disables the GameObject after it completes the animation, but it isn't working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelChanger : MonoBehaviour
{
public Animation anim;
public void SetTrigger()
{
this.StartCoroutine(this.PerformAnimRoutine());
}
private IEnumerator PerformAnimRoutine()
{
var state = anim.PlayQueued("Fade_In", QueueMode.PlayNow, PlayMode.StopSameLayer);
yield return new WaitForSeconds(state.length);
this.gameObject.SetActive(false);
}
}
Is there anything wrong with the code or is there an easier way to accomplish this? I am extremely new to unity so I am very stuck.
If all you are doing is fading a sprite to clear and you seem to know about coroutines, I might start by suggesting you do the fade within a coroutine instead.
Have that decrease the alpha by some fraction each frame and when it's 0 disable the object.
That's just if that sounds more fluid, nothing wrong with the animation way.
Doing it with animations though :
I'm not confident that you can disable the object the animation is on in that animation. If it is available on the dope sheet try that. Otherwise we can use state behaviours or animation events.
Animation Events
These can be used to trigger a function at a certain point of an animation. You can create them similar to keyframes. Here is a link to Unity's guide on this topic.
All you'd need to do is create an event and place it at the end of the animation. Then you need to in a script of that object make a public function that simply disables the object. Call that with the event.
State Behaviours
State Machine Behaviours allow you to define a script to run on a given animation state. It has many functions to hook onto such as OnStateEnter and OnStateExit.
You'd want to click on the state that fades in the animator. In the inspector you should be able to click "Add Behaviour". This will create a script that you can open and edit. Here is the reference for that class.
From there is should be very simple to disable the object through OnStateExit.
I want an enemy to look at the player when he shoots him, I call this function from an animation event:
public void ShootPlayer
{
thisTr.LookAt(playerTr);
thisTr.rotation = Quaternion.Euler(0, thisTr.rotation.eulerAngles.y, 0);
GameObject newArrow = Instantiate(straightFlightArrow, shootPoint.position, transform.rotation);
em.canMove = true;
}
It does get called, but model`s rotation does not change. The weird thing is, if I invoke thisTr.LookAt(playerTr) in Start(), the model will rotate accordingly during the shoot animation. Also, if I rotate the model from another script the same way before the shoot animation starts it will work as well.
So for some reason trying to rotate the model specifically from the animation event does not work for me. I have tried checking constraints on and off, applied and disabled root motion, but there is no effect. I am sure that there is some obvious mistake that I make, but I just can`t figure it out.
Properties that are being animated (i.e. inhibited by the animation engine) cannot be set from code. There's a workaround for it, but it's ugly - you need to do your update in LateUpdate() (so it's after the animation engine does it's transformations) and you have to keep track of the updated value, overwriting it each and every frame (because otherwise the engine will overwrite your next frame with what it had calculated).
Other workarounds involve wrapping GameObjects in empty GameObjects, i.e. my animator animated an object's position, which I wanted to change in code as well, so my structure was:
animatedContainer
|- actualObjectAlsoAnimatedFromCode
As far as I know this also applies to properties which are inhibited at some point, but not necessarily changed during current animation (and I think this is the issue your case is facing).
Thanks for reading. I tried posting this on unity answers, but it's been 24 hours and it's still not out of moderation, so maybe I'll have better luck here.
Background
I am very new to unity and know I must be missing some critical piece of knowledge to tie this together. I've created a blank game with some placeholder terrain and added a main camera to the 3d orthographic scene. I would like the user to be able to move the camera along a parallel plane to the ground.
What I've Done
I've added a starter camera codebehind I found online along with an event system, rigid body, ray caster, and a bunch of other things I saw online for this sort of thing. All of these components are in the same layer.
The Problem
My camera script's update method is hit when I put that in for testing but it doesn't receive any other events even though it implements the appropriate interfaces. I'm guessing I have something blocking the input from hitting the script or I do not have a component I need in order for those events to fire.
I'm testing on desktop for now but I want this to run on mobile (although from what I've read the events I'm subscribing to should be universal). Can anyone spot any oversights or deficiencies in my setup so far? Here are some screenshots.
Thanks!
My Setup
Edit: by request, here is the camera script I'm using. I know it doesn't do what I want yet but the issue I'd like to tackle right now is why it doesn't seem to be wired up correctly. Like I said, if I put an update method in here it is hit but none of my other logs are hit.
using UnityEngine;
using UnityEngine.EventSystems;
// The touch is in 2D but the scene is in 3D, so touch.x => scene.x, touch.y => scene.z, and nothing change (0) => scene.y since y is up
public class TouchCamera : MonoBehaviour,
IPointerDownHandler,
IDragHandler,
IPointerUpHandler
{
private Vector3 prevPointWorldSpace;
private Vector3 thisPointWorldSpace;
private Vector3 realWorldTravel;
private int drawFinger;
private bool drawFingerAlreadyDown;
public void OnPointerDown(PointerEventData data)
{
Debug.Log("Pointer down");
if (drawFingerAlreadyDown == true)
return;
drawFinger = data.pointerId;
drawFingerAlreadyDown = true;
prevPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
// in this example we'll put it under finger control...
GetComponent<Rigidbody>().isKinematic = false;
}
public void OnDrag(PointerEventData data)
{
Debug.Log("Pointer drag");
if (drawFingerAlreadyDown == false)
return;
if (drawFinger != data.pointerId)
return;
thisPointWorldSpace = data.pointerCurrentRaycast.worldPosition;
realWorldTravel = thisPointWorldSpace - prevPointWorldSpace;
_processRealWorldtravel();
prevPointWorldSpace = thisPointWorldSpace;
}
public void OnPointerUp(PointerEventData data)
{
Debug.Log("Pointer up");
if (drawFinger != data.pointerId)
return;
drawFingerAlreadyDown = false;
GetComponent<Rigidbody>().isKinematic = false;
}
private void _processRealWorldtravel()
{
Debug.Log("Updating camera position");
Vector3 pot = transform.position;
pot.x += realWorldTravel.x;
pot.z += realWorldTravel.y;
transform.position = pot;
}
}
Final Edit for the Solution
Thank you very much to Juan Bayona Beriso for helping me in chat with this configuration. Here is what we ended up with that now works:
The camera object has a Rigidbody and Physics 2D Raycaster attached to it. The scene now has a new Canvas object with a Graphics Raycaster (blocking mask set to everything and blocking objects set to none) and an event system with the standalone input module enabled. That canvas also has a child UI object that covers the screen and has the camera script attached. As a curiosity, it required a text component in order for the script to start receiving the events -- this only made a difference when the Raycast Target option was selected. Screenshots below.
I think you are misusing the EventSystem, OnPointerDown, OnDrag and all those functions, they are called when you have a PhysicsRaycaster in your Camera object and you press or drag over an UI Element or and object with a collider.
In your code you don't have any of these, you have a camera so you are not actually pointer down over it or dragging on it.
You have two options:
Either put this script in a plane with a collider or GUI element that is inside the camera and moves with it, or use Input.GetMouseButtonDown, Input.GetMouseButtonUp in the Update function