Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» Honors 104.502 Lab 2 – Snake

teaching machines

Honors 104.502 Lab 2 – Snake

February 5, 2016 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
honors gamedev, labs, spring 2016.

When people start making games, they usually start with TicTacToe. I can’t do that to you. TicTacToe, like War, gives the illusion of player interaction but is far too deterministic. Today we’ll implement the second game that many people make: Snake.

Find a parter and make sure you have access to a computer with Unity installed.

Checkpoint 1

Let’s start with Person A at the computer.

Snake Head

Create the snake’s head using Unity’s builtin cube GameObjects. Give it a distinct material. We’re going to treat the game world as a grid of 1×1 cells, so snap it to whole number coordinates.

Snake Egg

Create an egg using Unity’s builtin sphere GameObjects. Give it a distinct material. Snap it to whole number coordinates.

Don’t forget about Duplicate. If you have a source object kind of like the one you want to make, it’s often easier to Duplicate and tweak the clone. Especially if you remember the keyboard shortcut: Control/Command-D.

Movement

Let’s make the head move. Start by adding a script to the snake head GameObject. This gives the snake head a new behavior/component that we get to write. The naming convention for scripts is ThingController, where  Thing is replaced by the name of the entity you are controlling with this script.

In class we moved our objects with something like this in the Update method:

float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
transform.Translate(new Vector3(horizontal, vertical, 0));

The GetAxis method gives us a value in [-1, 1] that indicates how much pressure is being applied to the WASD keys, cursor keys, or joystick. It nicely smooths these pressure readings out to prevent sudden discontinuities. However, sometimes we want discontinuities. In Snake, let’s make the snake move from very discretely from cell to cell of a grid. Here’s the idea we’re after in pseudocode:

if w, a, s, or d is down
  offset = (0, 0, 0)
  if w is down
    offset.y = 1
  else if a is down
    offset.x = -1
  else if s is down
    offset.y = -1
  else
    offset.x = 1

  translate by offset

Let’s rewrite this in C# in your update method. You’ll need to know a few things to do this. First, instead of Input.GetAxis, let’s use Input.GetKeyDown. Feed it a string for the key you want to know about. It will give back true or false, depending on the “downness” of the key. Second, to create a new 3-vector variable at the origin, we say something like this:

Vector3 offset = new Vector3(0, 0, 0);

Third, an if statement has this form in C#:

if (some true/false expression) {
  // code to execute when condition is true
}

When we’re choosing between two actions, the code looks like this:

if (some true/false expression) {
  // code to execute when condition is true
} else {
  // code to execute when condition is false
}

When we’re choosing between three or more actions, the code looks like this:

if (expr1) {
  // code to execute when expr1 is true
} else if (expr2) {
  // code to execute when expr1 is false and expr2 is true
} else if (expr3) {
  // code to execute when expr1 and expr2 are false and expr3 is true
} ...
  ...
} else {
  // code to execute when none of the above conditions is true
}

Watch your curly braces and indentation! Keeping the code readable is important to understanding what you’ve written.

Playtest. Does the snake jump from cell to cell?

Triggers

When the snake runs into an egg, we want it to grow. When it runs into itself, we want game over. To detect these collisions, we need to add some tags and colliders to our objects.

Click on the egg. In the Inspector, click on the Tag button and add tags Egg and Segment. Click on the egg again and assign the Egg tag to the egg.

Remove the 3D sphere and box colliders from the egg and snake head. Add equivalent 2D colliders to each. Also add a Rigidbody to the snake head.

A component with a Rigidbody is normally managed by the physics system. We want more control. Check Is Kinematic on the snake head to declare that we are controlling its position, not the physics engine.

By default, colliders act as walls that stop other objects from passing through. That’s not the behavior we want for the egg. Check Is Trigger on the circle collider to make it act more like a switch than a wall.

Now, in the script you’ve started, we should be able to detect when the snake head lands on something with a trigger collider with OnTriggerEnter2D:

void OnTriggerEnter2D(Collider2D collider) {
  Debug.Log(collider.gameObject.tag);
}

If we hit an egg, let’s destroy it. How do we tell if we hit an egg? Let’s compare the tag with the known egg tag:

collider.gameObject.tag == "Egg"

This expression would make an excellent condition for an if statement. If we hit an egg, let’s destroy it:

Destroy(collider.gameObject);

We’ll make the snake grow in the next checkpoint.

Checkpoint 2

Person B types.

Prefabs

We’re going to want a lot of eggs. Duplicate isn’t a great idea here. If we need to a change a property of the eggs, we’ll need to change each one individually. Instead, let’s drag the egg from their hierarchy into the project assets. This makes its a prefab.

To add a second instance of this prefab, drag it from the project assets into the scene. And a third and fourth. And so on. Any changes made to the prefab will affect the instances.

Body Segments

Let’s make a snake body segment. Duplicate the snake head and rename it to something meaningful. Remove the Rigidbody and controller script, as these only apply to the snake head. Make it a trigger. Give it tag Segment.

We’re going to want many instances of it, so make a prefab out of it and delete it from the scene.

Segments List

Somehow we’re going to need to manage a list of snake body segments. This calls for some sort of data structure that can hold together a list of GameObjects. We’ll use what’s called a LinkedList. We’ll need to do a few things in your script to make this work.

At the top of the file, add this line after the similar lines already there:

using System.Collections.Generic;

Within the outermost set of curly braces (where we’ve been adding public variables), add a declaration for our list of segments:

public class HeadController : Monobehavior {
  private LinkedList<GameObject> segments;

  void ...
}

In the Start function, we need to give this list its initial assignment:

void Start() {
  segments = new LinkedList<GameObject>();
}

Moving

When the snake moves, we want to do two things—provided the snake has a body at all:

  1. Introduce a new body segment where the head just was.
  2. Remove the tail.

When the snake lands on an egg, we want to add a new body segment wherever the tail just was—or, if it doesn’t have a body, where the head just was. Let’s rig this logic up in Update:

on update
  if one of WASD down
    offset = ...
    
    oldPosition = transform.position
    translate by offset
    if snake has a body
      prepend new segment at oldPosition
      tailPosition = segment.last
      remove tail
    else
      tailPosition = oldPosition

To rewrite this in C#, we need to know a few things:

Growing

If we’ve set tailPosition correctly, we should be able to make a new segment when we hit an egg in OnTriggerEnter2D with this line:

segments.AddLast((GameObject) Instantiate(segmentTemplate, tailPosition, Quaternion.identity));

Self-collision

Expand OnTriggerEnter2D to also handle collisions with segments. When the snake collides with itself, we can delete the whole thing with this code:

foreach (GameObject segment in segments) {
  Destroy(segment);
}
segments.Clear();                                                                                                                                                                 
Destroy(gameObject);

Beyond

If you’re looking into more challenges:

  1. Add sounds using AudioClips and AudioSource. The program sfxr is pretty great for generating gratifying low quality sound effects.
  2. Add walls.
  3. Make or find images for the egg and snake body. Bring them into your scene as sprites, and use them instead of the builtin GameObjects.
  4. If you want to add an automatic timer, talk to me about coroutines.