How to change a sprite to another and then back after 1 second - unity3d

I'm developing a simple game in Unity2D, in which several monsters eat things that are dragged to them. If the right object is dragged to the monster, the score goes up by one and the monster should make a happy face, otherwise, score goes down and makes a sad face. This is the code I'm using for that (minus the transitions to happy/sad):
if (transform.name.Equals ("yellow")){
if (collinfo.name.Equals ("plastic(Clone)")) {
Debug.Log ("+1");
audio.Play ();
GameSetup.playerScore += 1;
gs.GetComponent<GameSetup>().removeit(aux);
}
else {
Debug.Log ("-1");
audio.Play ();
if (GameSetup.playerScore == 0)
{}
else
{
GameSetup.playerScore -= 1;
}
gs.GetComponent<GameSetup>().removeit(aux);
}
The audio played is just a 'munching' sound.
I want the monster to change sprites to happyFace (via GameObject.GetComponent ().sprite = happyFace), wait one second and then switch back to it's normal sprite, but don't know how to implement that waiting period.
Any and all help would be much appreciated.

This can be implemented several ways, however, I would use a method that returns an IEnumerator like so…
This assumes you have a variable in your script that has a reference to the SpriteRenderer that is attached to the GameObject with this script.
SpriteRenderer sr = GetComponent <SpriteRenderer> ();
I also assume you have an original sprite and the possible sprites to change to as variables too.
public Sprite originalSprite;
public Sprite happyFaceSprite;
public Sprite sadFaceSprite;
public IEnumerator ChangeFace (Sprite changeToSprite)
{
sr.sprite = changeToSprite;
yield return new WaitForSeconds (1.0f);
sr.sprite = originalFaceSprite;
}
You would then call this function with the applicable sprite as the variable.
if (happy)
StartCoroutine (ChangeFace (happyFaceSprite);
else
StartCoroutine (ChangeFace (sadFaceSprite);
Because the ChangeFace method returns an IEnumerator, we must call that function with the StartCoroutine function. The method will run until it reaches the yield return new WaitForSeconds (1.0f) function, then wait for 1.0f seconds and then resume where it stopped last time.
Understand?
Note
I haven't tested this but I don't see why it wouldn't work.

Put a floating point variable in your monster controller, call it happyTimer or something. It should start at zero.
Then in your Update function you should check happyTimer is above zero. If it is, then subtract Time.deltaTime and check again. If happyTimer is zero or below for the second check, then call your function that resets the sprite.
When you set the sprite to "happy face", also set happyTimer = 1. This will begin the countdown starting with the next Update call.
The relevant part of Update would look like this:
if(happyTimer > 0) {
happyTimer -= Time.deltaTime;
if(happyTimer <= 0) {
resetSprite();
}
}

Related

GameObject Follow cursor yet also follows enemies?

I'm making a simple character that follows the player's cursor. What I also want is for when the game object "enemy" appears the character then goes to that location to alert the player. Once the enemy is gone the character continues to follow the cursor like normal. Is there a reason why my script won't work. How else can I paraphrase it?
public class FollowCursor : MonoBehaviour
{
void Update ()
{
//transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
if (gameObject.FindWithTag == "Enemy")
{
GameObject.FindWithTag("Enemy").transform.position
}
if (gameObject.FindWithTag != "Enemy")
{
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
}
}
You are not using FindWithTag correctly, as it is a method that takes a string as parameter you need to use it like this:
GameObject.FindwithTag("Something") as stated in the Unity scripting API
Now to apply this to your code you would need to do the following to set your players position based on wether or not an enemy is found (assuming this script is on your actual player object):
if(GameObject.FindWithTag("Enemy"))
{
//If an enemy is found with the tag "Enemy", set the position of the object this script is attatched to to be the same as that of the found GameObject.
transform.position = GameObject.FindWithTag("Enemy").transform.position;
}
else
{
//when no enemy with the tag "Enemy" is found, set this GameObject its position to the the same as that of the cursor
transform.position = Camera.main.ScreenToWorldPoint( new Vector3(Input.mousePosition.x,Input.mousePosition.y,8.75f));
}
However this code will just snap your player instantly to the position of the found Enemy. If this is not the desired behaviour you could use a function like Vector3.MoveTowards instead to make the player move to it gradually.
This code also has room for optimisation as searching for a GameObject every update frame is not the ideal solution. But for now it should work.
I'm going to code coding all the function for you, I'm not pretty sure about the beavihour of your code, I understand a gameobject will be attached to the mouse position, so not really following....
Vector3 targetPosition;
public float step = 0.01f;
void Update()
{
//if there is any enemy "near"/close
//targetPosition = enemy.position;
//else
//targetPosition = MouseInput;
transform.position = Vector3.MoveTowards(transform.position, targetPosition , step);
}
For the f you can use a SphereCast and from the enemies returned get the closest one.

Is it safe to start a new coroutine inside a current coroutine?

I have a grid-based game in which I programmed my movement script to move my game objects cell by cell. To achieve the cell by cell movement that I want, I had to use coroutines.
Here's a pseudo-code snippet of my code:
private Coroutine currentCoroutine;
public void Move(Vector3 velocity)
{
currentCoroutine = StartCoroutine(MoveCoroutine(velocity));
}
private IEnumerator MoveCoroutine(Vector3 velocity)
{
Vector3 endPoint;
Vector3 nextVelocity;
if(velocity == Vector3.Right)
{
endPoint = rightEndPoint;
// object should move left in the next coroutine
nextVelocity = Vector3.Left;
}
else
{
endPoint = leftEndPoint;
// object should move right in the next coroutine
nextVelocity = Vector3.Right;
}
while(currentPos != endPoint)
{
currentPos += velocity
yield return new WaitForSeconds(movementDelay);
}
currentCoroutine = StartCoroutine(MoveCoroutine(nextVelocity));
}
Basically what this does is move my object left and right. If it reaches the left edge already, I make it go right and vice-versa. I call the Move() from another script.
This code is working fine for me. However, I am not sure if starting a new coroutine inside a coroutine is safe, like what I did here. Are there any consequences when writing coroutines like this? I'm still getting used to the concept of coroutines.
Thanks
Starting a new coroutine at the end of a coroutine is safe. By the way, I noticed that your yield statement is only called inside a while loop. If there's any chance the while loop won't run at least once, you'll have an error; all coroutines must execute a yield statement.

Auto attacking on collision

For a sidescrolling game I need to attack the enemies which collides with my player range, here is how I do :
void OnCollisionStay2D(Collision2D coll) {
Enemy target = coll.gameObject.GetComponent <Enemy> ();
if (target != null) {
moving = false;
anim.SetBool ("attack", true);
target.Damage (damageValue);
}
}
The problem is that i deal damageValue damages at each frame, so I wanted to add a cooldown, like this:
void OnCollisionStay2D(Collision2D coll) {
Enemy target = coll.gameObject.GetComponent <Enemy> ();
if (target != null && time.time > nextFire) {
moving = false;
anim.SetBool ("attack", true);
target.Damage (damageValue);
nextFire = Time.time + 1;//I set the cooldown to 1 second
}
}
The problem is that the player is attacking one time, and never again, I tried to Debug.Log(Time.time) at the end of OnCollisionStay method, and the debug spam is stopping just before the time where Time.time > nextFire;
I tried to Debug.Log at the start of the method, and it seems like OnCollisionStay is not called anymore after this debug freeze, which occurs around 1s - time.deltaTime after the first attack.
So I don't know how to do, basically here it is a 1D side-scrolling, you can just go from right to left, this should be really simple but I don't get it.
You may consider to create a fire rate for the weapon so it is changeable to one bullet at a time or serial fire. You will have control over it. Also prefer raycasting instead of collision. There is a 2D platformer tutorials around but I very liked the one from Brackeys.
YouTube Playlist

Can I loop a lot of falling objects in unity?

I make 7 cubes and I want to auto drop when the game starts. So I use Gravity to make objects fall. When the game start all cubes are dropped automatically but I want to make it happen again and again. I do not have any idea how to make it. So if you know, please tell me how to make it.
You can do it easily by attaching a script to falling objects to re-position them after going out of focus of the camera or reaching a specific location on the 3D space.
Some simple scripts like this would do,
//Change this to suit your needs
void Update()
{
if (renderer.IsVisibleFrom(Camera.main))
{
Debug.Log("Still Visible");
}
else
{
Debug.Log("Not visible");
transform.position = new Vector3(x, y, z);
}
}
This will not dynamically spawn object nor destroy it would rather reuse existing.
Make your cube into a prefab. Then create a GameObject somewhere in your scene and attach a script to it that spawns the cubes every x seconds.
public Transform MyPrefab;
private float timer = 0f;
void Update() {
timer += Time.deltaTime;
// Spawn a new block every second
if (timer > 1f) {
Instantiate(MyPrefab);
timer -= 1f;
}
}
In the inspector, drag your prefab to the MyPrefab property of your spawning object so it knows what to instantiate.
You should probably attach a script to your prefab cubes that calls Destroy() on them once they fall completely off screen.

How to make object move continously without stopping(unity2d)

So I've got this script here that moves the player by -1.25 when you click it but I want it to be continuously adding -1.25 until you release the button. Right now it only moves once when you click the button. My code:
var character : GameObject;
function OnMouseDown () {
character.GetComponent(Animator).enabled = true;
BlahBlah ();
}
function OnMouseUp () {
character.GetComponent(Animator).enabled = false;
}
function BlahBlah () {
character.transform.position.x = character.transform.position.x + -1.25;
}
Does anyone have any ideas? Thanks!
You're simply forgetting to work in the Update()
var character : GameObject;
function OnMouseDown ()
{
character.GetComponent(Animator).enabled = true;
}
function OnMouseUp ()
{
character.GetComponent(Animator).enabled = false;
}
function BlahBlah ()
{
// I added time.deltaTime, since you'll be updating the value every "frame",
// and deltaTime refers to how much time passed from the last frame
// this makes your movement indipendent from the frame rate
character.transform.position.x = character.transform.position.x - (1.25* time.deltaTime);
}
// The standard Unity3D Update method runs every frame
void Update()
{
if (character.GetComponent(Animator).enabled)
{
BlahBlah ();
}
}
What I did here is using Unity logic. Almost everything works in the Update() function, which gets called every frame. Note that frame rate can vary based on machine / complexity of scene, so make sure to use always Time.deltaTime when adding something related.
Another note for you: modifying the position directly won't make your object react to collisions ( so you'll be moving through objects, but you will still "trigger" the collision ). So, if you want to manage collision, remember to use physics!
you can use Input.GetMouseButton because it registers every frame so when you hold down the mouse it gets it but because it checks every frame while the mouse is down so your object will move so fast so you may want to add a timer that if the time is reached it moves so it moves a bit slower so we check if a specified amount of time that we set in timeLeft is passed and the mouse is held down then we move our object
float timeLeft=0.5f;
void Update(){
timeLeft -= Time.deltaTime;
if (Input.GetMouseButton(0)){
if(timeLeft < 0)
{
BlahBlah ();
}
}
}
void BlahBlah () {
timeLeft=0.5f;
character.transform.position.x = character.transform.position.x + -1.25;
}
As I remember onMouseDown fires every frame if you're holding the button, so you'll need to do the movement within that function, let me check one sec.