- Home /
Slime script?
I am making a 2d pixel art game and have been trying to create a script for a slime enemy. I need: When the character isn't in range, the slime just slides across the ground every few seconds, playing a movement animation. When it comes close to a gameobject (along the x axis) it needs to turn around and slide the other way.
When the player comes near the slime, I want the slime to jump towards the character, and if it hits, changes my health script.
If you can help, thanks...
Answer by IndievdB · Sep 18, 2017 at 04:14 AM
Hi there,
I think that people do not like broad questions like these because there's so much to cover and it comes off as asking other people to code for you for free. If you break problems like this into smaller steps like "How to tell if the player is in range?" and "How to move an object left and right in alternation?" people will be much more receptive to answering those questions. You will also find that the answers will reveal themselves to you without having ask for help! If you don't know how to break it down into smaller steps, you can always ask about how to do that, but be clear that you are asking for a breakdown and not free code.
That being said, I am sure you are just asking for guidance, so I'll give a detailed breakdown of how I would approach this problem. Some of the explanation below covers basic topics as I do not know your skill level (apologies in advance).
The Plan
1) Create the slime enemy in their idle state, moving left or right every few seconds and playing an animation.
2) Detect when GameObjects gets close to the slime enemy.
3) If the GameObject is not the character, have the slime move away.
4) If the GameObject is the character, trigger an attacking sequence.
Step 1
I suggest creating a GameObject with a SpriteRenderer to display the slime, a Rigidbody2D to give it physics, a BoxCollider2D to give it collision, and a new script called SlimeEnemy to control it's movements.
Within the script, there are plenty of ways to wait every few seconds in code, but I suggest Coroutines. Normally a function is run once in a single frame. Coroutines are functions that allow you start running code, wait for a few seconds, and continue were we left off. This can be difficult to grasp, so I highly suggest following the above link before continuing. Lastly, we can move our slime enemy by setting the velocity of its RigidBody2D directly. So far we have something like this:
using System.Collections;
using UnityEngine;
public class SlimeEnemy : MonoBehaviour
{
public float idleSpeed = 30;
private Coroutine slimeUpdate;
private Rigidbody2D body;
void Awake ()
{
// Get a reference to our physics component
body = GetComponent<Rigidbody2D> ();
}
void Start ()
{
// Start the idle coroutine
slimeUpdate = StartCoroutine (Idle());
}
IEnumerator Idle ()
{
int direction = 1;
while (true)
{
print ("This will print every 2 seconds");
// Move in a direction
body.velocity = ( new Vector2( idleSpeed * direction, 0) );
// Change the direction we move in
direction *= -1;
// Wait
yield return new WaitForSeconds (2);
}
}
}
Be sure to adjust your Rigidbody2D's linear drag and gravity to get the desired effect.
Step 2
We can use the OnTriggerEnter function to recognize when a GameObject gets close to us. Add a CircleCollider2D to the Slime Enemy and be sure to check the CircleCollider2D's "IsTrigger" checkbox to true. This CircleCollider2D will act as our range of vision. When another collider enters this range, our script's OnTriggerEnter function will be called. Adding the following function to our script will satisfy step 2:
void OnTriggerEnter2D (Collider2D other)
{
if (other.tag == "Player")
{
print ("the player is in range!");
}
else if (transform.position.y == other.transform.position.y)
{
print ("another object is in range!");
}
}
This code will recognize any GameObject with the tag "Player" or any object on the same vertical level when those objects get close enough. You might want to adjust this yourself, because it is unlikely that the objects you want to run from will be at EXACTLY the same Y position.
Step 3
This is the easiest step since we already know how to have our enemy slide from Step 1! The only difference is that our direction will be whichever way is opposite what we are moving away from:
else if (transform.position.y == other.transform.position.y)
{
print ("another object is in range!");
int direction = (this.transform.position.x > other.transform.position.x) ? 1 : -1;
body.velocity = ( new Vector2( idleSpeed * direction, 0) );
}
The line where we set direction uses a ternary operator. This line essentially means that if we are further along from the other object on the X axis, set direction to 1, otherwise, set direction to -1.
Step 4
Next is the most difficult step. If the player is in range, we need to continuously perform jumping attacks until they are out of range. The first thing to do is to switch from Idle mode to Agro mode. This can be done as follows:
void OnTriggerEnter2D (Collider2D other)
{
if (other.tag == "Player")
{
print ("the player is in range!");
chasingPlayer = true;
// Stop Idle
StopCoroutine (slimeUpdate);
// Start Agro
slimeUpdate = StartCoroutine (Agro(other.transform));
}
else if (transform.position.y == other.transform.position.y && !chasingPlayer)
{
print ("another object is in range!");
int direction = (this.transform.position.x > other.transform.position.x) ? 1 : -1;
body.velocity = ( new Vector2( idleSpeed * direction, 0) );
}
}
This is slightly complicated as it involves Coroutines, but we are stopping the Idle Coroutine and starting the Agro Coroutine, which we will create next. We pass the Agro Coroutine the transform of the player. One other thing. We don't want to run away from objects while we are chasing the player, so I created a global boolean called chasingPlayer that we will use to prevent this behavior.
Creating the Agro Coroutine will be almost exactly like creating the Idle Coroutine except that we will add some upwards velocity to mimic a jump and the direction we jump will be towards the player rather than alternating. For this, we can use the ternary operator we used earlier.
IEnumerator Agro (Transform player)
{
while (true)
{
// 1 if player is to the right of us, -1 if player is to the left of us
int direction = (this.transform.position.x < player.position.x) ? 1 : -1;
// Jump towards the player
body.velocity = ( new Vector2( agroSpeed * direction, jumpSpeed) );
yield return new WaitForSeconds (1);
}
}
Lastly, we need a way of returning to Idle if the Player escapes agro. This can be done with the OnTriggerExit2D function! This is the exact reverse of detecting if the player has entered our range:
void OnTriggerExit2D (Collider2D other)
{
if (other.tag == "Player")
{
print ("The player has escaped.");
chasingPlayer = false;
// Stop Agro
StopCoroutine (slimeUpdate);
// Start Idle
slimeUpdate = StartCoroutine (Idle());
}
}
Next Steps
You'll notice I didn't talk about animations and how to affect player health. This is something you can ask in a separate question or look up for yourself. Splitting problems like these into their individual steps and tackling them one at a time is fun and you will get better at it with practice. You will add tools to your arsenal, like Collider events and Coroutines and programming will become a lot easier.