Neil Creates: Don’t Starve

Don’t Starve is an uncompromising wilderness survival game full of science and magic. Enter a strange and unexplored world full of strange creatures, dangers, and surprises. Gather resources to craft items and structures that match your survival style. – Klei EntertainmentDon’t Starve Steam Page

Part 1: Set Up

First off, I made the sprite sheet for the game first in order to have something to work with, using Photoshop.

Capture.PNG

As you might notice, I only used one sheet for both the player and the environment assets. I did this only for the sake of the tutorial. If you’re going to make a game, it’s a great practice to separate the static (mostly environment and props) sprites from those with animations (characters, effects, etc.).

Capture

After finishing up with the sheet on Photoshop. I made a separate PNG file which I will then manipulate in Unity. Now, Unity can actually read .psd files right off the bat. But, I kept the .psd file of my sheet separate from what I actually used in Unity (I also do this with FBX and .blend files when doing 3D). This may add another step in the pipeline and I can’t really say any benefits from doing this, but for me, it’s another great practice to backup and keep all ‘raw’ or original files separate from the one being used in the engine.

Capture.PNG

As for slicing the sheet, you can use Unity’s built-in sprite editor or use a 3rd party application, whichever you feel more comfortable working with (I used Unity’s built-in sprite editor). Then, you can automate this process and you can also slice manually, especially if you have a specific requirement with the sprites. Also, I kept the pivot points of the sprites in the middle – this is not usually the case, especially with characters that have different sprites for different parts of their body.

For example, you have a character which is composed of separate sprites (shoulder, arms, hands, etc.) and you will be applying inverse kinematics where the hand is the target from the shoulder. In a character’s arm, you don’t want the pivot of the shoulder to be in the middle of the sprite ‘cut’; this will make it difficult to manipulate, because the rotation of the shoulder will be relative to where the pivot is, in this case, the shoulder will rotate weirdly. Instead, you want the pivot to be where the shoulder joint is; this way, the shoulder will ‘naturally’ move. It’s important to keep requirements like this in mind when slicing up sprite sheets in order to avoid going back and making changes which will be a pain, especially if you’re already in the middle of production.

Capture

I then added an Animator component to the character; this will hold the parameters (that we will be accessing via code) which will determine which animation will be played (idle, walking, and attacking). For producing the animations, I used Unity’s animation tab to cycle through my sprites.

You can say that, Unity pretty much has it all *wink*.

Capture

In order to control the animations, I set up the parameters and the transition logic of the animations in Unity’s Animator tab. I used a float as a parameter to transition between the ‘idle’ and the ‘walk’ animations, which may not be the best option since you can use a boolean to determine if the character is moving or not. But, I suggest that you get used to using floats because you can have a walking animation and a running animation, which will be determined via the speed/velocity – which is mostly a float – of your character. Then for the attack, I used a boolean because the player can be ‘attacking’ or ‘not attacking’.

Part 2: Programming

As mentioned in the video, there are multiple ways of moving objects in Unity:

  • transform.Translate()
  • RigidBody (Forces, VelocityChange, and MovePosition mostly)
  • Character Controller
  • Unity’s NavMesh (I don’t think this is a good idea, in main characters, but GREAT WITH AI)
  • Changing the object’s Transform component via code (works sometimes, but not advisable)

For our player, I added a rigidBody component and moved the sprite around in 3D space via VelocityChange.

Here are the codes:

For the player,


using UnityEngine;
public class PlayerScript : MonoBehaviour {

[SerializeField] //These are used to make the variables show up in the inspector
private float moveSpeed;
[SerializeField]
private float hitDistance;
private float currentSpeed;
private Animator anim;
private Rigidbody rb;

private int woodCount = 0;

private float maxAttackSpeed = 1.2f, attackTimer = 0;

void Start () {
   anim = GetComponent<Animator>();
   rb = GetComponent<Rigidbody>();
}

private void Update() {
 if (Input.GetMouseButton(0)) {
  interact();
  anim.SetBool("isAttacking", true);
 } else {
  anim.SetBool("isAttacking", false);
 }
}

void FixedUpdate () {
 if (Input.GetButton("Horizontal")) {
  currentSpeed = Input.GetAxis("Horizontal") * moveSpeed * Time.deltaTime;

  if(currentSpeed < 0) {
   GetComponent<SpriteRenderer>().flipX = true;
  } else {
   GetComponent<SpriteRenderer>().flipX = false;
  }

  rb.AddForce(currentSpeed * transform.right, ForceMode.VelocityChange);
 }else if (Input.GetButton("Vertical")) {
  currentSpeed = Input.GetAxis("Vertical") * moveSpeed * Time.deltaTime;
  rb.AddForce(currentSpeed * transform.forward , ForceMode.VelocityChange);
 } else {
  anim.SetFloat("Speed", 0f);
  rb.velocity *= 0.5f;
 }

 if(currentSpeed != 0) {
  anim.SetFloat("Speed", rb.velocity.magnitude);
 }
}

private void interact() {
 RaycastHit hit;

 if(attackTimer > maxAttackSpeed) {
  if(Physics.Raycast(transform.position, Mathf.Sign(currentSpeed) * Vector3.right, out hit, hitDistance)) {
   if (hit.transform.CompareTag("Tree")) {
    woodCount++;
   }
  }

  attackTimer = 0;
 } else {
  attackTimer += Time.deltaTime;
 }
}
}

 

For the Camera,


using UnityEngine;

public class CameraFollowScript : MonoBehaviour {

private Transform player;
[SerializeField]
private float panSpeed;
[SerializeField]
private float height;
[SerializeField]
private float distance;

private RaycastHit hit;
private Ray rayFromCamera;

void Start () {
player = GameObject.FindGameObjectWithTag("Player").transform;
}

void Update () {
rayFromCamera = new Ray(transform.position, transform.forward);

if(Physics.Raycast(rayFromCamera, out hit, 3f)) {
if (!hit.transform.CompareTag("Player")) {
var obstacle = hit.transform.gameObject;
if (obstacle.GetComponent<SpriteRenderer>() != null) {
obstacle.AddComponent<ChangeAlpha>();
}
}
}

var newPosition = new Vector3(player.position.x, player.position.y + height, player.position.z - distance);
transform.position = Vector3.Lerp(transform.position, newPosition, panSpeed * Time.deltaTime);
}
}

For changing the alpha of the object between the camera and the player,


using UnityEngine;

public class ChangeAlpha : MonoBehaviour {

private SpriteRenderer currentSprite;

void Start() {
currentSprite = GetComponent<SpriteRenderer>();

var currentColor = currentSprite.color;
currentColor.a = 0.8f;

currentSprite.color = currentColor;
}

void Update() {
var currentColor = currentSprite.color;
currentColor.a = 1f;

currentSprite.color = currentColor;
Destroy(this);
}
}

There is a flaw in my logic here. You might have noticed that when you play the game, it the ChangeAlpha code works. But, it is not efficient – this is a wrong implementation, if I will be honest.

Why? Because, the script is CONTINUOUSLY adding and removing the script on the object that is between the camera and the player which consumes a lot of resources. This is also the reason why you can’t see the component in the inspector.

Short explanation: the ChangeAlpha script is being added to the object in the CameraFollowScript then it is being removed in the Update function (which runs every frame) in the ChangeAlpha script.

Having triggers behind the obstacles may be a better option. Triggers that detects if the player (tagged as “Player) is behind the object, then changing the alpha of the obstacle, if so.

There you have it, I hope you enjoyed the tutorial. There were errors here and there and that is the beauty of learning – we make mistakes and we learn from them.

If you have comments, suggestions, recommendations, and questions, comment them down below and let’s have a healthy discussion.

Thank you and see you in the next one!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s