Advanced Game Design

Camel Code Review

Here are examples of the “Camel” game. Our goal here is to do code reviews on this code.

Before the code review, think about:

  1. First, list what are the goals of the code review.

  2. Look for common mistakes. Keep a to-do list.

    1. Can a person drink more water than is in the canteen?

    2. Do we mis-calculate how far back the people are?

    3. Can the chasing people skip past the person and miss seeing them?

    4. Can we both win and lose the game at the same time? Or otherwise get conflicting messages?

  3. Quantify effectiveness of your code review. (Bugs found, changes made, etc.)

  4. Code reviews often include work on unit-tests. We aren’t doing that here but keep it in mind.

  5. Code reviews should be less than 400 lines and 60 minutes.

Camel Version 1

  1using System;
  2
  3namespace CamelGame
  4{
  5	class Program
  6	{
  7		private static void Choices()
  8		{
  9			Console.WriteLine(" ");
 10			Console.WriteLine("A. Drink from your canteen.");
 11			Console.WriteLine("B. Ahead moderate speed.");
 12			Console.WriteLine("C. Ahead full speed.");
 13			Console.WriteLine("D. Stop and rest.");
 14			Console.WriteLine("E. Status check.");
 15			Console.WriteLine("Q. Quit.");
 16			Console.WriteLine(" ");
 17		}
 18		private static void DistanceTraveled(int camelMovement)
 19		{
 20			Console.WriteLine(" ");
 21			Console.WriteLine("You traveled " + camelMovement + " miles.");
 22		}
 23		private static void ChoiceA(ref int thirst, ref int drinks)
 24		{
 25			if (drinks > 0)
 26			{
 27				drinks -= 1;
 28				thirst = 0;
 29			}
 30			else Console.WriteLine("You are out of water.");
 31		}
 32		private static void ChoiceB(ref int milesTraveled, ref int thirst, ref int camelTiredness, ref int drinks, ref int nativeDistance, Random random)
 33		{
 34			int camelMovement = random.Next(5, 12);
 35			int nativeMovement = random.Next(7, 14);
 36			milesTraveled += camelMovement;
 37			thirst += 1;
 38			camelTiredness += 1;
 39			nativeDistance += nativeMovement;
 40			int oasisFound = random.Next(1, 20);
 41			if (oasisFound == 1)
 42			{
 43				Oasis(out thirst, out camelTiredness, out drinks);
 44			}
 45			DistanceTraveled(camelMovement);
 46		}
 47		private static void ChoiceC(ref int milesTraveled, ref int thirst, ref int camelTiredness, ref int drinks, ref int nativeDistance, Random random)
 48		{
 49			int camelMovement = random.Next(10, 20);
 50			int nativeMovement = random.Next(7, 14);
 51			int addTiredness = random.Next(1, 3);
 52			milesTraveled += camelMovement;
 53			thirst += 1;
 54			camelTiredness += addTiredness;
 55			nativeDistance += nativeMovement;
 56			int oasisFound = random.Next(1, 20);
 57			if (oasisFound == 1)
 58			{
 59				Oasis(out thirst, out camelTiredness, out drinks);
 60			}
 61			DistanceTraveled(camelMovement);
 62		}
 63		private static void ChoiceD(ref int nativeDistance, ref int camelTiredness, Random random)
 64		{
 65			Console.WriteLine("The Camel is happy");
 66			int nativeMovement = random.Next(7, 14);
 67			nativeDistance += nativeMovement;
 68			camelTiredness = 0;
 69		}
 70		private static void ChoiceE(int milesTraveled, int drinks, int nativeDistance)
 71		{
 72			Console.WriteLine("Miles traveled: " + milesTraveled);
 73			Console.WriteLine("Drinks in canteen: " + drinks);
 74			Console.WriteLine("The natives are " + (milesTraveled - nativeDistance) + " miles behind you.");
 75		}
 76		private static void Oasis(out int thirst, out int camelTiredness, out int drinks)
 77		{
 78			Console.WriteLine("You found an oasis!");
 79			Console.WriteLine("The camel is rested and your thirst and canteen are replenished.");
 80			thirst = 0;
 81			drinks = 3;
 82			camelTiredness = 0;
 83		}
 84		static void Main(string[] args)
 85		{
 86			int milesTraveled = 0;
 87			int thirst = 0;
 88			int camelTiredness = 0;
 89			int drinks = 3;
 90			int nativeDistance = -20;
 91			string choice;
 92			string playAgain;
 93			bool done = false;
 94			Random random = new Random();
 95
 96			Console.WriteLine("Welcome to Camel!");
 97			Console.WriteLine("You have stolen a camel to make your way across the great Mobi desert.");
 98			Console.WriteLine("The natives want their camel back and are chasing you down! Survive your desert trek and out run the natives.");
 99
100			while (!done)
101			{
102				Choices();
103				Console.Write("Enter Choice: ");
104				choice = Console.ReadLine();
105				if (string.Equals("Q", choice.ToUpper()))
106				{
107					done = true;
108				}
109
110				else if (string.Equals("A", choice.ToUpper()))
111				{
112					ChoiceA(ref thirst, ref drinks);
113				}
114
115				else if (string.Equals("B", choice.ToUpper()))
116				{
117					ChoiceB(ref milesTraveled, ref thirst, ref camelTiredness, ref drinks, ref nativeDistance, random);
118				}
119
120				else if (string.Equals("C", choice.ToUpper()))
121				{
122					ChoiceC(ref milesTraveled, ref thirst, ref camelTiredness, ref drinks, ref nativeDistance, random);
123				}
124
125				else if (string.Equals("D", choice.ToUpper()))
126				{
127					ChoiceD(ref nativeDistance, ref camelTiredness, random);
128				}
129
130				else if (string.Equals("E", choice.ToUpper()))
131				{
132					ChoiceE(milesTraveled, drinks, nativeDistance);
133				}
134
135				if (thirst > 6)
136				{
137					Console.WriteLine("You died of thirst.");
138					done = true;
139				}
140
141				else if (thirst > 4)
142				{
143					Console.WriteLine("You are thirsty.");
144				}
145
146				if (camelTiredness > 8 & !done)
147				{
148					Console.WriteLine("Your camel is dead.");
149					done = true;
150				}
151
152				else if (camelTiredness > 5)
153				{
154					Console.WriteLine("Your camel is getting tired.");
155				}
156
157				if (milesTraveled - nativeDistance <= 0 & !done)
158				{
159					Console.WriteLine("The natives caught you!");
160					done = true;
161				}
162
163				else if (milesTraveled - nativeDistance <= 15)
164				{
165					Console.WriteLine("The natives are getting close!");
166				}
167
168				if (milesTraveled >= 200 & !done)
169				{
170					Console.WriteLine("You escaped the natives!");
171					done = true;
172				}
173
174				if (done)
175				{
176					Console.Write("Play Again? (Y/N) ");
177					playAgain = Console.ReadLine();
178					if (string.Equals("Y", playAgain.ToUpper()))
179					{
180						done = false;
181					}
182					else if (string.Equals("N", playAgain.ToUpper()))
183					{
184						Console.WriteLine("Thanks for playing!");
185					}
186				}
187			}
188		}
189	}
190}

Camel Version 2

  1using System;
  2using System.Collections;
  3using System.Collections.Generic;
  4
  5namespace Camel
  6{
  7    class Program
  8    {
  9        // Initialize variables for use throughout the entire program
 10        static int playerPosition;
 11        static int hadesPosition;
 12        static int gameLength;
 13        static bool done;
 14        static int energy;
 15        static int maxEnergy = 20;
 16        static int drachmas;
 17        static int maxShops = 4;
 18        static int hadesMovement;
 19        static int turnCounter;
 20        static int positionDifference;
 21        static string distanceStatement;
 22        static string energyStatement;
 23        static int[] shops;
 24        static int movementModifier;
 25        static int hadesMovementModifier;
 26        static Dictionary<string, int> shopItems;
 27        static int speedBoostTurns;
 28        static int maxSpeedBoost = 5;
 29        static int roadBlockTurns;
 30        static int maxRoadBlock = 5;
 31        static bool purchaseMade;
 32        static bool moved;
 33        static bool exitShop;
 34        static string lineBreak = "-------------------------------------------------------------------------------------";
 35
 36        static void Main(string[] args)
 37        {
 38            // ----------------------------------------- SETUP FUNCTIONS FOR GAME USE -----------------------------------------
 39            void InitializeGame()
 40            {
 41                // Initialize game variables and general setup.
 42                hadesPosition = 0;
 43                // Set player position relative to Hades at the beginning of the game.
 44                playerPosition = SetPlayerPosition(hadesPosition);
 45                // Randomize the length that the player must travel.
 46                SetGameLength();
 47                // Get our list of shop locations.
 48                shops = DisperseShops();
 49                energy = maxEnergy;
 50                turnCounter = 0;
 51                // This is used to keep our game running until the player wins or is caught by Hades.
 52                done = false;
 53                hadesMovementModifier = 0;
 54                movementModifier = 0;
 55                shopItems = new Dictionary<string, int>();
 56                InitializeShop();
 57
 58                string gameStartTutorial;
 59                gameStartTutorial = "You are in ancient Greece and have just completed an undercover recon mission for Zeus. Hades has discovered " +
 60                    "what you have done and is now chasing you while you make your way back to Olympus! Get back to Olympus before Hades makes you pay " +
 61                    "the price!";
 62
 63                Console.WriteLine(gameStartTutorial);
 64                Console.WriteLine(lineBreak);
 65            }
 66
 67            // This function will set the Game's length every time it is started up.
 68            int SetGameLength()
 69            {
 70                gameLength = RandomNumber(45, 60);
 71                return gameLength;
 72            }
 73
 74            // Function for generating random numbers within the game.
 75            int RandomNumber(int min, int max)
 76            {
 77                Random random = new Random();
 78                return random.Next(min, max);
 79            }
 80
 81            // This function sets the players initial starting position with respect to the enemy's position.
 82            int SetPlayerPosition(int hadesLocation)
 83            {
 84                int playerLocation;
 85                playerLocation = hadesLocation + RandomNumber(1, 6);
 86                return playerLocation;
 87            }
 88
 89            int SetHadesPosition()
 90            {
 91                hadesMovement = RandomNumber(2, 5);
 92                if (hadesMovement - hadesMovementModifier < 0)
 93                {
 94                    hadesMovement = 0;
 95                }
 96                else
 97                {
 98                    hadesMovement = hadesMovement - hadesMovementModifier;
 99                }
100                hadesPosition += hadesMovement;
101                return hadesPosition;
102            }
103
104            int[] DisperseShops()
105            {
106                int previousShop;
107                int interval;
108                int[] shopLocations = new int[maxShops];
109
110                previousShop = 0;
111
112                for (int i = 0; i < maxShops; i++)
113                {
114                    interval = RandomNumber(4, 10);
115                    if (previousShop + 4 < gameLength && (previousShop + interval) < gameLength)
116                    {
117                        shopLocations[i] = previousShop + interval;
118                        previousShop = shopLocations[i];
119                    }
120                }
121
122                return shopLocations;
123            }
124
125            string GetEnergyStatement(int energy)
126            {
127                if (energy <= 3)
128                {
129                    return "You are exhausted...";
130                }
131                else if (energy > 3 && energy <= 10)
132                {
133                    return "You are starting to get tired...";
134                }
135                else if (energy > 10 && energy <= 15)
136                {
137                    return "You still feel moderately energetic.";
138                }
139                else
140                {
141                    return "You are pulsing with energy!";
142                }
143            }
144
145            void CheckForAncientRuins()
146            {
147                int random;
148                int drachmasGained;
149                random = RandomNumber(0, 26);
150                drachmasGained = RandomNumber(5, 15);
151                if (random == 12)
152                {
153                    Console.WriteLine("You have stumbled upon some ancient ruins. You have found " + drachmasGained + " drachmas and your energy has been restored!");
154                    drachmas += drachmasGained;
155                    energy = maxEnergy;
156                }
157            }
158
159            void Purchase(string itemName, int price)
160            {
161                string shopStatement = "";
162
163                if (itemName.Equals("Energy Drink") && drachmas >= price)
164                {
165                    energy = maxEnergy;
166                    shopStatement = "Your energy has been replenished!";
167                    shopItems.Remove("Energy Drink");
168                    drachmas -= price;
169                    purchaseMade = true;
170                }
171                else if (itemName.Equals("Speed Boost") && drachmas >= price)
172                {
173                    movementModifier = RandomNumber(2, 4);
174                    speedBoostTurns = maxSpeedBoost;
175
176                    shopStatement = "Your movement speed has been boosted by " + movementModifier +
177                        " for " + speedBoostTurns + " turns!";
178                    shopItems.Remove("Speed Boost");
179                    drachmas -= price;
180                    purchaseMade = true;
181                }
182                else if (itemName.Equals("Road Block") && drachmas >= price)
183                {
184                    hadesMovementModifier = RandomNumber(1, 3);
185                    roadBlockTurns = maxRoadBlock;
186                    shopStatement = "You have injured Hades and have restricted his movement by " +
187                        hadesMovementModifier + " for " + roadBlockTurns + " turns!";
188                    shopItems.Remove("Road Block");
189                    drachmas -= price;
190                    purchaseMade = true;
191                }
192                else if (drachmas < price)
193                {
194                    Console.WriteLine("You cannot afford that!");
195                    purchaseMade = false;
196                }
197                else
198                {
199                    Console.WriteLine("You have decided to leave.");
200                    exitShop = true;
201                }
202                if (purchaseMade)
203                {
204                    Console.WriteLine("You have purchased one " + itemName);
205                    Console.WriteLine(shopStatement);
206                }
207            }
208
209            void OpenShop()
210            {
211                string shopStatement = "You have stumbled upon a traveling merchant!";
212                int i = 1;
213                Console.WriteLine(shopStatement);
214                Dictionary<int, string> itemList = new Dictionary<int, string>();
215
216                foreach (KeyValuePair<string, int> item in shopItems)
217                {
218                    string displayItem = i + ". " + item.Key + " -------- " + item.Value;
219                    Console.WriteLine(displayItem);
220                    itemList.Add(i, item.Key);
221                    i += 1;
222                }
223
224                string leave = i + ". Leave";
225                itemList.Add(i, "Leave");
226                string playerDrachmas = "Your current held drachmas: " + drachmas;
227                string buy = "Would you like to make a purchase?";
228                Console.WriteLine(leave);
229                Console.WriteLine(playerDrachmas);
230                Console.WriteLine(buy);
231
232                purchaseMade = false;
233                exitShop = false;
234
235                while (!purchaseMade && !exitShop)
236                {
237                    string userInput = Console.ReadLine();
238
239                    try
240                    {
241                        string itemName;
242                        int price;
243
244                        itemList.TryGetValue(Convert.ToInt32(userInput), out itemName);
245                        shopItems.TryGetValue(itemName, out price);
246                        Purchase(itemName, price);
247                        exitShop = true;
248                    }
249                    catch
250                    {
251                        Console.WriteLine("Please enter a valid number.");
252                    }
253                }
254            }
255
256            void InitializeShop()
257            {
258                shopItems.Add("Energy Drink", 10);
259                shopItems.Add("Speed Boost", 25);
260                shopItems.Add("Road Block", 15);
261            }
262
263            void IncrementItemDuration()
264            {
265                if (speedBoostTurns > 0)
266                {
267                    speedBoostTurns -= 1;
268                }
269                if (roadBlockTurns > 0)
270                {
271                    roadBlockTurns -= 1;
272                }
273            }
274
275            void GetUserDecision()
276            {
277                string slow;
278                string medium;
279                string fast;
280                string rest;
281                string input;
282                string status;
283                string quit;
284                bool decided;
285                int drachmasGained = 0;
286
287                decided = false;
288
289                slow = "1. Slow and Steady...";
290                medium = "2. Keep a moderate pace.";
291                fast = "3. Full steam ahead!!!";
292                rest = "4. Stop and take a rest...";
293                status = "5. Journey Status.";
294                quit = "6. Quit Game.";
295
296                if (positionDifference >= 10)
297                {
298                    Console.WriteLine("Hades is very far away...");
299                }
300                else if (positionDifference >= 6)
301                {
302                    Console.WriteLine("Hades is getting closer.");
303                }
304                else
305                {
306                    Console.WriteLine("Hades is right on your tail!!!");
307                }
308                Console.WriteLine();
309                Console.WriteLine(slow + "\n" + medium + "\n" + fast + "\n" + rest + "\n" + status + "\n" + quit);
310                Console.WriteLine("What would you like to do?");
311
312                while (!decided)
313                {
314                    // Get user input and set up if statements to evaluate user input.
315                    input = Console.ReadLine();
316
317                    int distanceTraveled;
318                    int energyUsed;
319
320                    // If player has decided to take it slow...
321                    if (input.Equals("1"))
322                    {
323                        distanceTraveled = (RandomNumber(1, 3) + movementModifier);
324                        energyUsed = RandomNumber(-3, -1);
325                        playerPosition += distanceTraveled;
326                        distanceStatement = "You have decided to play it safe, and have traveled " + distanceTraveled + " miles. ";
327                        energyStatement = GetEnergyStatement(energy);
328                        drachmasGained = RandomNumber(1, 3);
329
330                        // Make sure we are not surpassing the maximum energy cap.
331                        if (energy - energyUsed < maxEnergy - energyUsed)
332                        {
333                            energy -= energyUsed;
334                        }
335                        else
336                        {
337                            energy = maxEnergy;
338                        }
339
340                        IncrementItemDuration();
341
342                        decided = true;
343                    }
344                    // If player has decided on medium travel speed...
345                    else if (input.Equals("2") && energy >= 4)
346                    {
347                        distanceTraveled = (RandomNumber(3, 5) + movementModifier);
348                        energyUsed = RandomNumber(2, 4);
349                        playerPosition += distanceTraveled;
350                        energy -= energyUsed;
351                        distanceStatement = "You have decided to move at a steady pace, and have traveled " + distanceTraveled + " miles. ";
352                        energyStatement = GetEnergyStatement(energy);
353                        drachmasGained = RandomNumber(2, 4);
354                        drachmas += drachmasGained;
355                        IncrementItemDuration();
356
357                        decided = true;
358                    }
359                    // If player has decided to go full speed...
360                    else if (input.Equals("3") && energy >= 8)
361                    {
362                        distanceTraveled = (RandomNumber(4, 8) + movementModifier);
363                        energyUsed = RandomNumber(6, 8);
364                        drachmasGained =
365                        playerPosition += distanceTraveled;
366                        energy -= energyUsed;
367                        distanceStatement = "You have decided to travel at full speed, and have traveled " + distanceTraveled + " miles. ";
368                        energyStatement = GetEnergyStatement(energy);
369                        drachmasGained = RandomNumber(3, 6);
370                        IncrementItemDuration();
371
372                        decided = true;
373                    }
374                    // If player has decided to rest...
375                    else if (input.Equals("4"))
376                    {
377                        energyUsed = RandomNumber(-10, -6);
378                        energy -= energyUsed;
379                        distanceStatement = "You have decided to take a rest and recover your energy. ";
380                        energyStatement = GetEnergyStatement(energy);
381                        IncrementItemDuration();
382
383                        decided = true;
384                    }
385                    else if (input.Equals("5"))
386                    {
387                        Console.WriteLine("----------------- STATUS REPORT -----------------\nEnergy: " + GetEnergyStatement(energy) +
388                            "\nDrachmas: " + drachmas + "\nHades is " + positionDifference + " miles behind you.");
389                        decided = false;
390                    }
391                    else if (input.Equals("6"))
392                    {
393                        bool decisionMade = false;
394                        Console.WriteLine("Are you sure you would like to quit?\n1. Yes\n2. No");
395                        string choice;
396
397                        while (!decisionMade)
398                        {
399                            choice = Console.ReadLine();
400
401                            if (choice.Equals("1"))
402                            {
403                                Console.WriteLine("You have exited the game.");
404                                done = true;
405                                decided = true;
406                                decisionMade = true;
407                            }
408                            else if (choice.Equals("2"))
409                            {
410                                decisionMade = true;
411                            }
412                            else
413                            {
414                                Console.WriteLine("Please enter either 1 or 2.");
415                            }
416                        }
417                    }
418                    // If player has entered anything that is not above or does not have enough energy...
419                    else
420                    {
421                        if ((input.Equals("2") && energy < 4) || (input.Equals("3") && energy < 8))
422                        {
423                            Console.WriteLine("You do not have enough energy for that!");
424                        }
425                        else
426                        {
427                            Console.WriteLine("That is not an option, please enter a number between 1 and 4.");
428                        }
429                        // Keep loop running.
430                        decided = false;
431                    }
432                }
433
434                // Update Hades position and print out the game status.
435                if (!done)
436                {
437                    SetHadesPosition();
438                    Console.WriteLine(distanceStatement + energyStatement);
439                    Console.WriteLine("While traveling you found " + drachmasGained + " drachmas!");
440                    drachmas += drachmasGained;
441                    turnCounter += 1;
442                }
443
444                for (int i = 0; i < shops.Length; i++)
445                {
446                    if (playerPosition == shops[i])
447                    {
448                        OpenShop();
449                    }
450                }
451            }
452            // -----------------------------------------------------------------------------------------------------------------------
453
454            // ----------------------------------------------------MAIN GAME SETUP----------------------------------------------------
455            InitializeGame();
456
457            // Run this loop while the game is not over.
458            while (!done)
459            {
460                string input;
461                bool playAgainDecision;
462                if (playerPosition < gameLength)
463                {
464                    if (playerPosition <= hadesPosition)
465                    {
466                        Console.WriteLine("You were caught! Game Over!");
467                        playAgainDecision = false;
468                        Console.WriteLine("Would you like to try again?\n1. Yes\n2. No");
469
470                        while (!playAgainDecision)
471                        {
472                            input = Console.ReadLine();
473
474                            if (input.Equals("1"))
475                            {
476                                InitializeGame();
477                                playAgainDecision = true;
478                                done = false;
479                            }
480                            else if (input.Equals("2"))
481                            {
482                                playAgainDecision = true;
483                                done = true;
484                            }
485                            else
486                            {
487                                Console.WriteLine("Invalid Input. Please enter either 1 or 2.");
488                                playAgainDecision = false;
489                            }
490                        }
491                    }
492                    else
493                    {
494                        CheckForAncientRuins();
495                        GetUserDecision();
496                        positionDifference = playerPosition - hadesPosition;
497                        Console.WriteLine(lineBreak);
498                    }
499                }
500                else
501                {
502                    Console.WriteLine("You win!");
503                    playAgainDecision = false;
504                    while (!playAgainDecision)
505                    {
506                        input = Console.ReadLine();
507
508                        if (input.Equals("1"))
509                        {
510                            InitializeGame();
511                            playAgainDecision = true;
512                            done = false;
513                        }
514                        else if (input.Equals("2"))
515                        {
516                            playAgainDecision = true;
517                            done = true;
518                        }
519                        else
520                        {
521                            Console.WriteLine("Invalid Input. Please enter either 1 or 2.");
522                            playAgainDecision = false;
523                        }
524                    }
525                }
526            }
527        }
528        // -----------------------------------------------------------------------------------------------------------------------
529    }
530}

Camel Version 3

  1using System;
  2using System.Collections.Generic;
  3using System.Linq;
  4using System.Text;
  5using System.Threading.Tasks;
  6
  7namespace CamelGame
  8{
  9    class Program
 10    {
 11        private static readonly int MILES_TO_HIDEOUT = 200;
 12
 13        private static bool done;
 14        private static bool win;
 15        private static bool quit;
 16        private static int milesTraveled;
 17        private static int fillupsLeft;
 18        private static int policeMilesTraveled;
 19        private static int gasTankLeft;
 20        private static char userInput;
 21        private static bool validInput;
 22        private static readonly Random rand = new Random();
 23
 24        static bool FoundOasis(int findingNumber)
 25        {
 26            if (findingNumber == 15)
 27            {
 28                return true;
 29            }
 30            else
 31            {
 32                return false;
 33            }
 34        }
 35
 36        static void Main()
 37        {
 38            bool playAgain = true;
 39
 40            while (playAgain)
 41            {
 42                Console.WriteLine("Welcome to Bank Heist!\n" +
 43                "You have stolen one-million dollars from a bank and must escape to your secret hide out.\n" +
 44                "The police are hot on your tail and will stop at nothing to catch you!\n" +
 45                "Out run the cops and escape to your hideout to keep your freedom.\n");
 46
 47                done = false;
 48                win = false;
 49                validInput = true;
 50
 51                milesTraveled = 0;
 52                gasTankLeft = 0;
 53                fillupsLeft = 3;
 54
 55                policeMilesTraveled = -20;
 56
 57
 58                while (!done)
 59                {
 60                    Console.WriteLine();
 61                    Console.WriteLine("A. Ahead moderate speed.\n" +
 62                        "B. Ahead full speed.\n" +
 63                        "C. Stop to fill up the gas tank.\n" +
 64                        "D. Status check.\n" +
 65                        "Q. Quit.\n");
 66
 67                    Console.Write("What is your choice? ");
 68                    userInput = Console.ReadKey().KeyChar;
 69                    Console.WriteLine("\n");
 70
 71                    validInput = true;
 72
 73                    // The user chooses to quit the game.
 74                    if (char.ToUpper(userInput) == 'Q')
 75                    {
 76                        QuitGame();
 77                    }
 78                    // The user chooses to check their status.
 79                    else if (char.ToUpper(userInput) == 'D')
 80                    {
 81                        CheckStatus();
 82                    }
 83                    // The user chooses to hide for the night.
 84                    else if (char.ToUpper(userInput) == 'C')
 85                    {
 86                        StopToFillGas();
 87                    }
 88                    // The user chooses to move ahead full speed.
 89                    else if (char.ToUpper(userInput) == 'B')
 90                    {
 91                        MoveAhead(false);
 92                    }
 93                    // The user chooses to move ahead slowly.
 94                    else if (char.ToUpper(userInput) == 'A')
 95                    {
 96                        MoveAhead(true);
 97                    }
 98                    // The user input was invalid.
 99                    else
100                    {
101                        Console.WriteLine("You input was invalid.");
102                        validInput = false;
103                    }
104
105                    CheckIfCaught();
106
107                }
108
109                if (win)
110                {
111                    Console.WriteLine("\nCongratulations! You've escaped the police and won the game!");
112                }
113                else if (!win && !quit)
114                {
115                    Console.WriteLine("\nYou have lost the game.");
116                }
117                else
118                {
119                    Console.WriteLine("\nThanks for playing.");
120                }
121
122                Console.Write("Would you like to play again? (Y/N) ");
123                userInput = Console.ReadKey().KeyChar;
124                Console.WriteLine("\n");
125
126                if (char.ToUpper(userInput) == 'Y')
127                {
128                    playAgain = true;
129                }
130                else if (char.ToUpper(userInput) == 'N')
131                {
132                    playAgain = false;
133                }
134                else
135                {
136                    Console.WriteLine("You have entered an invalid value and the game will now close.\n");
137                    playAgain = false;
138                }
139            }
140
141            Console.WriteLine("Thank you for playing. Press any key to exit.");
142            _ = Console.ReadKey();
143        }
144
145        private static void CheckIfCaught()
146        {
147            if (char.ToUpper(userInput) != 'Q' && validInput)
148            {
149                if (gasTankLeft > 8 && !done)
150                {
151                    Console.WriteLine("Your car ran out of gas and you got caught.");
152                    done = true;
153                }
154                else if (gasTankLeft > 5)
155                {
156                    Console.WriteLine("Your gas is getting low.");
157                }
158
159                if ((milesTraveled - policeMilesTraveled) <= 0 && !done)
160                {
161                    Console.WriteLine("The police caught you.");
162                    done = true;
163                }
164                else if ((milesTraveled - policeMilesTraveled) <= 15)
165                {
166                    Console.WriteLine("The police are getting close!");
167                }
168
169                if (milesTraveled >= MILES_TO_HIDEOUT && !done)
170                {
171                    done = true;
172                    win = true;
173                }
174            }
175        }
176
177        static void QuitGame()
178        {
179            done = true;
180            quit = true;
181        }
182
183        static void CheckStatus()
184        {
185            Console.WriteLine("Miles traveled: " + milesTraveled);
186            Console.WriteLine("Gas fill-ups remaining: " + fillupsLeft);
187            Console.WriteLine("The police are " + (milesTraveled - policeMilesTraveled) +
188                " miles behind you.");
189        }
190
191        static void StopToFillGas()
192        {
193            gasTankLeft = 0;
194            fillupsLeft -= 1;
195            policeMilesTraveled += rand.Next(7, 15);
196
197            if (policeMilesTraveled < milesTraveled)
198            {
199                Console.WriteLine("Your gas tank is full.");
200            }
201        }
202
203        static void MoveAhead(bool slow)
204        {
205            int currentMilesTraveled;
206            if (!slow)
207            {
208                currentMilesTraveled = rand.Next(10, 21);
209                gasTankLeft += rand.Next(1, 4);
210            }
211            else
212            {
213                currentMilesTraveled = rand.Next(5, 13);
214                gasTankLeft++;
215            }
216            milesTraveled += currentMilesTraveled;
217            policeMilesTraveled += rand.Next(7, 15);
218            Console.WriteLine("You traveled " + currentMilesTraveled + " miles.");
219
220            int findingAHideout = rand.Next(19);
221
222            if (FoundOasis(findingAHideout) && milesTraveled < MILES_TO_HIDEOUT)
223            {
224                Console.WriteLine("You found an abandoned hideout!");
225                fillupsLeft = 3;
226                gasTankLeft = 0;
227            }
228        }
229
230    }
231}

Blender to Unity

Our goal is to learn to create simple, low-poly 3D items in Blender. Color them. Then import to Unity.

_images/trees_in_unity.png

Note

Don’t forget to Blender scale interface up before showing this tutorial so people can see.

Set Up Unity

  • Create a new 3D project in Unity

  • Create a 20x20 dark green plain for the ground

Create 3D Items in Blender

  • Open Blender

  • Notice window and hierarchy, like Unity

  • Navigation

    • Click-left to select

    • Middle click to rotate around focused object (Unity is alt-left click)

    • Number pad . to change focus (Unity is F key)

    • Shift-Middle button pans (unity is just left click if you are in the ‘hand tool’ mode)

    • Show axis thing in upper right to select side views. Also show num pad

  • Delete everything. We don’t want to import a camera or light.

  • We will be creating one file for each object.

    • Select item in hierarchy or screen, then delete key

  • Add->Mesh->Cylinder

    • Lower left, expand out window and select 0.5 meters and 12 verts

  • Edit object

    • Explain object mode, and edit mode. Use tab to switch

    • Show vert, edge, face select tools

    • Show alt-click to get circle

    • Show ‘E’ to extend.

    • Show ‘S’ to scale.

    • Show ‘G’ to move.

    • Show xyz to select axis

    • Make pine tree. Show how to scale to zero.

    _images/pine_in_blender.png

Materials in Blender

  • Materials

    • Show how to create a material for leaves

    • Assign it.

    • Can’t see it! Show select material view. And other views.

    • Create new material for trunk

    • Now need to assign. Show wireframe, face select, hidden faces.

Import in Unity

  • Back to unity

    • Create folder for blender models

    • Open in explorer. Copy path

  • Back to blender, save to path

    • pine tree

  • Unity & blender native files

    • Export to FBX

  • Back to Unity.

    • Drag file into scene

    _images/pine_in_unity.png

More Practice

  • Repeat, but create a tree using an icosphere. Add apples.

    • Show Ctrl-L for selecting linked

    • Shift-D to duplicate

    • Brand new blender file, do not combine

    • Watch scale

    _images/apple_tree.png

Weekend Assignment

Mixamo to Unity

This covers how to get a 3D character into your scene, using Mixamo character assets and animations.

_images/example.png

Mixamo has a few character assets (not its primary purpose) and a lot of animations for characters (its primary purpose).

Setup

Our goal here is to create a landscape for our character to walk around. We’ll add a plane and have a few cubes to help with a sense of distance and perspective.

  1. Create a new Unity 3D project

  2. Add a plane, name it “Ground”

  3. Scale x/z to 10x10

  4. Create a grass material color

  5. Add material to plane

  6. Create a cube

  7. Create a different material and add to cube

  8. Add rigid body physics to cube. Test.

  9. Duplicate a few cubes

  10. Position the camera

  11. Don’t forget to save

_images/setup.png

Download the Character from Mixamo

  1. Go to Mixamo.com

  2. Log in. You’ll log in with an Adobe id or some SSO choice they have.

Get the Character

Once there, go to the “Character” tab and find a character you like. I’m using Claire.

_images/get_character.png
  1. Select your character

  2. Hit “Download”

  3. You want “FBX for Unity”. You do not want the generic FBX it defaults to.

  4. Make sure T-Pose is selected

  5. Download

Get the Animations

Now we need our idle and walking animations.

_images/get_idle.png
  1. Switch to “Animations”

  2. Search on “Idle”

  3. Select an idle animation. If you don’t see it play with your character hit “refresh” on the browser. You can adjust the animation. For example, widen out the hands so they don’t clip through the characer.

  4. Click “Download”

  5. Select FBX for Unity. (Again, the default FBX doesn’t work.)

  6. Select “Without Skin” because we already downloaded that.

Next, repeat for a walking animation. You’ll get an extra check-box for in-place which you must check. This will keep the animation from moving the character forward, while the code thinks the character is in the same location.

Warning

You must select “In-Place” checkbox for any moving animation

Add Mixamo Characters and Animations to Project

Now we want to get the character to appear in our project.

  1. Create a folder for your character. In this case, I used “Claire”.

  2. Create subfolders for “Materials” and “Textures”

    _images/claire_folder.png
  3. Drag the character and two animations from your ‘downloads’ to the folder you created.

  4. Drag the character from the assets to your scene. It will be white, as no textures ore materials have been applied yet.

    _images/white_character.png
  5. Next click on your character in Assets.

  6. Select Materials in the Inspector panel.

  7. Click “Extract Textures” and put them in the Textures folder we created.

  8. Click “Extract Materials” and put them in the Materials folder we created.

    _images/extract_materials.png
  9. If you get a message like this, just go ahead and fix.

    _images/fix.png
  10. Now your character should look good.

    _images/t-pose.png

Get Character to Move

Now we need to get the character to move around. We are going to use a character controller. It is more complex than rigid body physics, but offers more control.

Add Character Controller

  1. Select your character.

  2. Select “Add Component” in the inspector.

  3. Add a character controller.

  4. The character controller has a ‘capsule’ for hitbox calculations. Adjust the size and positioning of this so it goes around your character.

    _images/character_controller.png

Add Character Script

  • Make the camera a ‘child’ of the player and position behind the player.

  • Add this character script:

 1using System.Collections;
 2using System.Collections.Generic;
 3using UnityEngine;
 4
 5public class CharacterScript : MonoBehaviour
 6{
 7    [SerializeField] Transform playerCamera = null;
 8    [SerializeField] float mouseSensitivity = 3.5f;
 9    [SerializeField] float walkSpeed = 6.0f;
10    [SerializeField] float gravity = -13.0f;
11    [SerializeField] [Range(0.0f, 0.5f)] float moveSmoothTime = 0.3f;
12    [SerializeField] [Range(0.0f, 0.5f)] float mouseSmoothTime = 0.03f;
13
14    [SerializeField] bool lockCursor = true;
15
16    float cameraPitch = 0.0f;
17    float velocityY = 0.0f;
18    CharacterController controller = null;
19
20    Vector2 currentDir = Vector2.zero;
21    Vector2 currentDirVelocity = Vector2.zero;
22
23    Vector2 currentMouseDelta = Vector2.zero;
24    Vector2 currentMouseDeltaVelocity = Vector2.zero;
25
26    void Start()
27    {
28        controller = GetComponent<CharacterController>();
29        if (lockCursor)
30        {
31            Cursor.lockState = CursorLockMode.Locked;
32            Cursor.visible = false;
33        }
34    }
35
36    void Update()
37    {
38        UpdateMouseLook();
39        UpdateMovement();
40    }
41
42    void UpdateMouseLook()
43    {
44        Vector2 targetMouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
45
46        currentMouseDelta = Vector2.SmoothDamp(currentMouseDelta, targetMouseDelta, ref currentMouseDeltaVelocity, mouseSmoothTime);
47
48        cameraPitch -= currentMouseDelta.y * mouseSensitivity;
49        cameraPitch = Mathf.Clamp(cameraPitch, -90.0f, 90.0f);
50
51        playerCamera.localEulerAngles = Vector3.right * cameraPitch;
52        transform.Rotate(Vector3.up * currentMouseDelta.x * mouseSensitivity);
53    }
54
55    void UpdateMovement()
56    {
57        Vector2 targetDir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
58        targetDir.Normalize();
59
60        currentDir = Vector2.SmoothDamp(currentDir, targetDir, ref currentDirVelocity, moveSmoothTime);
61
62        if (controller.isGrounded)
63            velocityY = 0.0f;
64
65        velocityY += gravity * Time.deltaTime;
66
67        Vector3 velocity = (transform.forward * currentDir.y + transform.right * currentDir.x) * walkSpeed + Vector3.up * velocityY;
68
69        controller.Move(velocity * Time.deltaTime);
70
71    }
72}
  • While the character does not animate yet, it should be able to move with mouse and WASD keys.

Animate

Add Armature Rigs

  • Select your character in the assets folder.

  • In the “Inspector” tab, select “Rig”.

  • Select “Humanoid”

  • Select “Create From This Model”.

  • Select “Apply”

    _images/character_rig.png
  • Select the “Idle” animation.

  • In the “Inspector” tab, select “Rig”.

  • Select “Humanoid”

  • Select “Copy From Other Avatar”.

  • Double-click on “Source” and select the avatar you just created

  • Select “Apply”

  • Repeat for the “Walk” animation.

  • There may be warnings. That’s ok.

Add Idle Animation

  1. Click on your character folder in assets, and add an Animator Controller.

    _images/add_animator_controller.png
  2. Double click on the animator controller to edit it. Then drag the idle animation to the controller.

  3. Drag the animator controller to your player object. Run. The player should now display the idle animation.

Add Speed Parameter

We will need to transition from idle to walking based on speed. We need to update our character controller to spit this out. Here’s our updates:

 1using System.Collections;
 2using System.Collections.Generic;
 3using UnityEngine;
 4
 5public class CharacterScript : MonoBehaviour
 6{
 7    [SerializeField] Transform playerCamera = null;
 8    [SerializeField] float mouseSensitivity = 3.5f;
 9    [SerializeField] float walkSpeed = 6.0f;
10    [SerializeField] float gravity = -13.0f;
11    [SerializeField] [Range(0.0f, 0.5f)] float moveSmoothTime = 0.3f;
12    [SerializeField] [Range(0.0f, 0.5f)] float mouseSmoothTime = 0.03f;
13    Animator _animator;
14
15    [SerializeField] bool lockCursor = true;
16
17    float cameraPitch = 0.0f;
18    float velocityY = 0.0f;
19    Vector3 velocity = Vector3.zero;
20
21    CharacterController controller = null;
22
23    Vector2 currentDir = Vector2.zero;
24    Vector2 currentDirVelocity = Vector2.zero;
25
26    Vector2 currentMouseDelta = Vector2.zero;
27    Vector2 currentMouseDeltaVelocity = Vector2.zero;
28
29    void Start()
30    {
31        _animator = GetComponentInChildren<Animator>();
32        controller = GetComponent<CharacterController>();
33        if (lockCursor)
34        {
35            Cursor.lockState = CursorLockMode.Locked;
36            Cursor.visible = false;
37        }
38    }
39
40    void Update()
41    {
42        UpdateMouseLook();
43        UpdateMovement();
44        float speedPercent = velocity.magnitude / walkSpeed;
45        _animator.SetFloat("speed", speedPercent);
46    }
47
48    void UpdateMouseLook()
49    {
50        Vector2 targetMouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
51
52        currentMouseDelta = Vector2.SmoothDamp(currentMouseDelta, targetMouseDelta, ref currentMouseDeltaVelocity, mouseSmoothTime);
53
54        cameraPitch -= currentMouseDelta.y * mouseSensitivity;
55        cameraPitch = Mathf.Clamp(cameraPitch, -90.0f, 90.0f);
56
57        playerCamera.localEulerAngles = Vector3.right * cameraPitch;
58        transform.Rotate(Vector3.up * currentMouseDelta.x * mouseSensitivity);
59    }
60
61    void UpdateMovement()
62    {
63        Vector2 targetDir = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
64        targetDir.Normalize();
65
66        currentDir = Vector2.SmoothDamp(currentDir, targetDir, ref currentDirVelocity, moveSmoothTime);
67
68        if (controller.isGrounded)
69            velocityY = 0.0f;
70
71        velocityY += gravity * Time.deltaTime;
72
73        velocity = (transform.forward * currentDir.y + transform.right * currentDir.x) * walkSpeed + Vector3.up * velocityY;
74
75        controller.Move(velocity * Time.deltaTime);
76
77    }
78}

Now in the Animator, we should be able to add speed:

_images/add_speed.png

Then we can add in our “walk” animation. Add transitions, and make it based on speed. Greater than 0.3, we animate. Less than 0.3, we idle.

_images/transition.png

Right now, the animations will only run once. Double-click between both animations and make sure that “Loop Time” box is checked for both animations.

_images/add_loop.png

Also, the animations won’t transition until they are done. Flip between both animations and uncheck “Has Exit Time.”

_images/has_exit_time.png

Uncheck Root Motion

Depending on your animation, the animation can move the character. Typically it works best if it is just an animation. Select your character, and in the “Animator” section, uncheck root motion:

_images/root_motion.png

References

Texture Objects

We want to put an image on an object, rather than just have a solid color.

Texture Types

There are several types of textures.

  • Diffuse/Albedo map - Color for object. The is the basics of what you need. Although the image can look “flat.” Think bricks. Shouldn’t look flat, but will be with just a diffuse map.

  • Bump maps - Create illusion of depth via grayscale data. Shade of gray is height. These are grayscale images.

  • Normal maps - Better than bump maps, uses RGB for more info. This can give us x, y, and z. Allows for angle and more realistic looks. These maps tend to look blue.

  • Displacement/Height map - This map is used to actually changes surface they are on.

  • Specular/Metallic - Maps out what part of the image is shiny.

Here are some samples from Texturise, their “Tilable Wood Planks Texture”.

_images/tileable_wood_planks_texture.jpg

Texture

_images/tileable_wood_planks_texture_NORMAL.jpg

Normal

_images/tileable_wood_planks_texture_SPECULAR.jpg

Specular

_images/tileable_wood_planks_texture_DISP.jpg

Displacement

_images/image1.png

Albido/Texture image/Color

_images/image2.png

Normal

_images/image3.png

Displacement

_images/image4.png

Specular

_images/image5.png

Everything

Here they are, in action on Blender.

Texture Websites

Where can you get textures?

Very Simple Textures

  • Create a new project.

  • Add a 10x10 plane.

  • Create a folder called “Textures”

  • Toss the images there.

  • Create a material in that folder.

  • Toss onto the plane.

  • Put images into texture

    • Toss ‘texture’ to Albedo.

    • Toss ‘specular’ to ‘metalic’. Change to ‘Albedo Alpha’ and turn smoothness down to about 0.1. You can use this for occlusion instead.

    • Toss ‘normal’ to ‘normal map’

    • Toss ‘displacement’ to ‘Height map’

_images/creating_material.png
  • You can change the ‘tiling’ to control how many times it repeats on the surface.

UV Mapping

Take some road textures:

_images/Tileable_cracked_asphalt_road_texture.jpg

Road texture

_images/Tileable_cracked_asphalt_road_texture_NORMAL.jpg

Road texture normal

_images/Tileable_cracked_asphalt_road_texture_SPECULAR.jpg

Road texture specular

Create a road texture. I used specular for occlusion. Apply to a new cube.

_images/road_cube.png

Looks ok. But what if we scale the cube?

_images/stretched_road_cube.png

We need to change the geometry, and not scale the item. Then do a “UV Unwrap”.

Go to blender. Create a cube. Go into edit mode and not object mode. Change the cube dimensions.

_images/blender_cube.png

Change the bottom view to UV. Do a smart UV unwrap:

_images/blender_uv.png

Delete camera and light. Save into your Assets folder. Toss cube onto scene. Apply material. See how it maps?

Change mapping. Save. See results.

_images/blender_uv2.png _images/unity_mapped.png

2D Unity Part 1

Create sample sprites and add to Unity

  1. Clone the base Unity project: https://github.com/pvcraven/2022_Class_2D_Project

  2. Create sprites in Aseprite.

    • Use NES palette

    • Create a 16x16 character.

    _images/paul_character.png
    • Create a 16x32 tree. (Or some other size, keeping in mind 16x16 is the character size.)

    _images/paul_tree.png
    • Save to Assets/Sprites/Trees or Assets/Sprites/Characters folder.

    • Call your character tree_name or character_name. Obviously, use your first and/or last name, not “name”.

    • Export your sprite as a .png in that same folder.

  3. Open in Unity, confirm the assets are there.

  4. Do a git add, commit, push and pull to sync with the whole class.

Warning

Be careful of .meta files

Unity adds a .meta file that tags a GUID for each file. If you create or move a file into a Unity project, let unity create a .meta for it before check in! This includes the exported .png. Failure to do this will cause a lot of merge headaches.

Change sprite settings

  1. Create your own scene. Call it scene_name.

  2. Drag character onto the screen.

  3. Way too small. Unity defaults to 100 pixels to one ‘unit’ which is 1 meter. Change from 100 to 16.

  4. Great. Now the character is blurry. Change the filtering to ‘point’.

  5. Character might be blotchy. Turn off compression.

  6. Should be able to run the scene and see character properly.

  7. Repeat these steps for your sprites. Don’t do this for other people’s sprites.

  8. Sync with GitHub.

Make sprites solid

  1. Add a rigid body 2d. Run the game. Character should now fall.

  2. Zero out the gravity.

  3. Add to your character, the MyCharacterController script that is already in the project under the scripts folder. Examine the script and see how it works.

  4. Should be able to move character with WSAD. Can adjust speed as needed.

  5. Add your tree.

  6. Try running. No collision.

  7. Add colliders to the character and tree.

    • There are circle colliders, capsule colliders, box colliders. Pick the best one.

    • You might not want to make a collider around everything for a more 3D look.

    _images/tree_collider.png
  8. Try running. Character spins!

  9. Freeze rotation.

    _images/freeze_rotation.png
  10. Character may or may not appear behind/ahead of the tree properly. You can use sort mode in project settings to fix:

    _images/sort_order.png

Add in score

Add in a sprite to increase your score.

  • You’ll need a collider. Make the collider a “trigger”.

  • You’ll need to add in the ScoreScript. Examine this script and the character controller together to see how they work.

  • Set the points for the score script.

    _images/score.png
  • Test.

  • You can also have items that make the score go down by putting in a negative number for points.

Add in scene change

Create a sprite that will will cause you to go to the next level.

  • You’ll need a collider. Make the collider a “trigger”.

  • You’ll need to add in the SceneChangeScript. Examine this script and the character controller together to see how they work.

  • Your scene must appear in File…Build Settings. This is where you determine the order of levels. As this is a common area, only one person can edit at a time. So let the instructor do this in class.

    _images/build_settings.png

Summary

This should step you through most of what you need to complete 2D Assignment 1. Expand what you’ve learned to create an explorable level. Don’t worry about the background image yet, we’ll get to that with tiles.

2D Unity Part 2

Create tile set

Tiles will be 16x16. We’ll make multiple tiles at a time. Make a 16*3 and 16*4 image:

_images/tileset.png

Keep in mind in Aseprite you can:

  • Use things like 16*3 in the sprite dimensions, no need to multiply itself.

  • You can show the grid overlay

Import and split tile set

When you import the sprite, we need to set our standard three changes, and then set it to multiple sprites. Then we click on the nearly-hidden sprite editor button and slice it up.

_images/sprite_editor.png
  • Commit and push.

Create tile map and palette

  • Create a new rectangular tile map for your scene.

  • Open the tile palette.

  • Create your own tile palette with your own name

_images/create_tilemap.png

Create a new palette. Create a new folder for it “Tile Palette”.

_images/create_palette.png

Select your sprites. Move to palette. Create folder for “Tile images”.

_images/palette_1.png

Order is weird. Somehow there’s a way to import better I think, but I don’t know it. To change order, click ‘Edit’ button and then alternate between S and M keys to move tiles to where you’d like.

_images/palette_1.png
  • Paint with the tiles.

  • Change your rendering order so tiles appear below your sprites. Use layers, or ordering in layers.

_images/paint_tiles.png
  • Show how to do layers

  • Show how to do a tile collider 2d

2D Animation

In this tutorial we’ll work on animating sprites.

Create a time-based animation in Aseprite

Create a folder for your animation.

Follow one of these tutorials:

https://64.media.tumblr.com/3c0287fd61ebbe01450b857a5536688a/tumblr_pj7xw9oPbZ1x0hs8ho1_540.gifv

Source: SadFace-RL Fire Animations

https://64.media.tumblr.com/f38af30218383e32db327943e6a039e2/tumblr_pjru2fQEYf1x0hs8ho1_540.gifv

Source: SadFace-RL Water Animations

Work on using:

  • Keyboard shortcuts

  • Select tool

  • Frames

Export a sprite sheet.

  • File->Export Sprite Sheet

  • Output->Output file

Import a sprite sheet in Unity

Import a sprite sheet and slice it like we did before.

_images/import_sprite_sheet.png
  • Drag the first image onto your scene.

  • Click Window…Animation

  • Click your object, you should see an option to create an animation and controller from it.

  • Drag images onto the timeline

  • Too fast.

  • Drag out the frames, slow it down

https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/676c614f-d09c-4485-a246-d1ea708273bd/dchb7sq-c8fedde8-961e-47d4-a4a3-495014265fc1.gif?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzY3NmM2MTRmLWQwOWMtNDQ4NS1hMjQ2LWQxZWE3MDgyNzNiZFwvZGNoYjdzcS1jOGZlZGRlOC05NjFlLTQ3ZDQtYTRhMy00OTUwMTQyNjVmYzEuZ2lmIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.jprvx0joe-4mDnN-XqmEEps-bOklrD1zm01bJIqLadA

Source: SadFace-RL Animation, getting started

Create animated character frames in Aseprite

https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/676c614f-d09c-4485-a246-d1ea708273bd/dcisa01-b285151f-0b33-4df3-b42a-641daddf0f2b.gif?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzY3NmM2MTRmLWQwOWMtNDQ4NS1hMjQ2LWQxZWE3MDgyNzNiZFwvZGNpc2EwMS1iMjg1MTUxZi0wYjMzLTRkZjMtYjQyYS02NDFkYWRkZjBmMmIuZ2lmIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.5A4ky15XzfRqitDMjOxaX0MGbBoYd-UWAg9kcCOLlb4

Source: SadFace-RL Characters, the human male

https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/676c614f-d09c-4485-a246-d1ea708273bd/dceoc95-b39fd3b3-61c9-48ca-8bc0-5f1218641935.gif?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzY3NmM2MTRmLWQwOWMtNDQ4NS1hMjQ2LWQxZWE3MDgyNzNiZFwvZGNlb2M5NS1iMzlmZDNiMy02MWM5LTQ4Y2EtOGJjMC01ZjEyMTg2NDE5MzUuZ2lmIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.HlW8ziFKqTJ0j78jhxsw-5UAPJP7QXqbIkXmmNfXDCk

Source: SadFace-RL Animation, the walk cycle

Get character working with idle animation in Unity

Here’s a video that covers what we are doing:

First, go ahead an import your character animations, then slice up the images.

If you want to replace a character you already have with the animated sprites, (recommended) you can replace the character’s texture by dragging the sprite image to the proper location.

_images/replace_sprite.png

Make sure your program still works ok.

Create an idle animation for your character like we did before.

_images/create_animation.png

Make sure that works. Now we need a clip for walking/running. Add a new clip from the Animator tab:

_images/new_clip.png

Show how to play clip, and change clip.

See how both clips show up in Animator.

Add a parameter, and transitions:

_images/transitions.png

Update code:

 1using System.Collections;
 2using System.Collections.Generic;
 3using UnityEngine;
 4using UnityEngine.SceneManagement;
 5
 6
 7public class MyAnimatedCharacterController : MonoBehaviour
 8{
 9    public int score = 0;
10
11    Rigidbody2D body;
12
13
14    float horizontal;
15    float vertical;
16    float moveLimiter = 0.7f;
17
18    public float runSpeed = 5.0f;
19
20    public AudioSource sound;
21    public AudioSource scoreIncreaseSound;
22    public AudioSource scoreDecreaseSound;
23
24    private SpriteRenderer spriteRenderer;
25    private Animator animator;
26
27    void Start()
28    {
29        // Get the rigid body component for the player character.
30        // (required to have one)
31        body = GetComponent<Rigidbody2D>();
32        spriteRenderer = GetComponent<SpriteRenderer>();
33        animator = GetComponent<Animator>();
34    }
35
36    void Update()
37    {
38        // Get our axis values
39        horizontal = Input.GetAxisRaw("Horizontal"); 
40        vertical = Input.GetAxisRaw("Vertical"); 
41    }
42
43    void FixedUpdate()
44    {
45
46        // If player is running diagonally, we don't want them to move extra-fast.
47        if (horizontal != 0 && vertical != 0) // Check for diagonal movement
48        {
49            // limit movement speed diagonally, so you move at 70% speed
50            horizontal *= moveLimiter;
51            vertical *= moveLimiter;
52        }
53
54        if (horizontal > 0.1)
55            spriteRenderer.flipX = false;
56        else
57            spriteRenderer.flipX = true;
58
59        // Set player velocity
60        body.velocity = new Vector2(horizontal * runSpeed, vertical * runSpeed);
61        animator.SetFloat("HorizontalSpeed", Mathf.Abs(horizontal));
62    }
63
64    void OnTriggerEnter2D(Collider2D colliderEvent)
65    {
66        // Did we run into an object that will affect our score?
67        ScoreScript scoreObject = colliderEvent.gameObject.GetComponent(typeof(ScoreScript))
68                                  as ScoreScript;
69
70        if (scoreObject != null)
71        {
72            // Yes, change the score
73            score += scoreObject.points;
74
75           
76            // Destroy the object
77            Destroy(colliderEvent.gameObject);
78        }
79
80        // Did we run into an object that will cause a scene change?
81        SceneChangeScript sceneChangeObject = colliderEvent.gameObject.GetComponent(typeof(SceneChangeScript))
82                                              as SceneChangeScript;
83        if (sceneChangeObject != null) {
84            // Yes, get our current scene index
85            int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
86            // Load up the scene accourding to the sceneChange value
87            UnityEngine.SceneManagement.SceneManager.LoadScene(currentSceneIndex + sceneChangeObject.sceneChange);
88        }
89    }
90    void OnGUI()
91    {
92        // Dispaly our score
93        GUIStyle guiStyle = new GUIStyle(GUI.skin.label);
94        guiStyle.fontSize = 32; //modify the font height
95        GUI.Label(new Rect(10, 10, 250, 50), "Score: " + score, guiStyle);
96    }
97}

2D Shooting

There are three main ways to shoot things in Unity.

  • For laser-types of things where you insta-hit, you can use ray-casting.

  • You can move sprites and check by distance.

  • You can move sprites, and use colliders. This is how we are going to demo things here.

Make a sprites in Aseprite

We need a target and a projectile.

  • Make a bullet, laser, heart, whatever projectile you want to shoot.

  • Make a target to hit.

  • Export, and import into Unity changing the normal three things. (pixels per unit, compression, filter)

_images/target_projectile.png

Detect mouse down events

Now, we will use the mouse button to shoot. First, we need to detect mouse-down events.

In our Update method on cour controller (not FixedUpdate, doesn’t seem to work well), we can detect a mouse-down event with Input.GetMouseButtonDown(0). The 0 is for our left mouse button. An implementation might look like:

// Has the mouse been pressed?
if (Input.GetMouseButtonDown(0))
{
    Debug.Log("Mouse down");
}

Code and confirm it works.

Create a bullet

Now we need something to shoot.

  • Create a bullet prefab.

  • Add a box collider so we can detect collisions. Set the collider to be a trigger, as we don’t want it bumping into things.

  • Add a rigidbody so we can move it via physics.

_images/bullet_prefab.png

Go to your character controller cand add a public variable for the prefab. Code would look like:

public GameObject bulletPrefab;

Then drag the prefab into the new blank spot in your character.

_images/game_object.png

Update code to fire the bullet:

// Mouse pressed?
if (Input.GetMouseButtonDown(0))
{
    // Make a bullet
    var bullet = Instantiate(bulletPrefab, body.position, Quaternion.identity);
    // Get the body of the bullet
    var bulletbody = bullet.GetComponent<Rigidbody2D>();
    // Move the bullet to the right
    bulletbody.velocity = new Vector2(4, 0);
}

It would be better code if you make the speed a public variable rather than hard-code it. And we’ll get to aiming in a bit.

Create targets

Now we need something to shoot. Create targets. Add a collider. Add a tag for “Destroyable”.

_images/destroyable.png

Add a bullet script to destroy

This bullet script will destroy itself after moving 8 units, or it will destroy an object tagged ‘destroyable’.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BulletScript : MonoBehaviour
{
    Vector3 _origin;
    public float maxDistance = 8.0f;

    // Start is called before the first frame update
    void Start()
    {
        // Get position we started at, so we can see how far the bullet traveled.
        _origin = transform.position;
    }


    public void OnTriggerEnter2D(Collider2D collision)
    {
        Debug.Log("Trigger");
        if (collision.tag == "Destroyable")
        {

            Debug.Log("Destroyable");
            // Destroy item we hit
            Destroy(collision.gameObject);
            // Cause bullet to destroy itself
            // Put this outside the if to get deleted when hitting non-destroyable objects
            Destroy(gameObject);
        }
    }

    // Update is called once per frame
    void Update()
    {
        // How far has the bullet gone?
        float distance = Vector2.Distance(_origin, transform.position);
        // If too far, then remove ourselves from the game.
        if (distance > maxDistance)
        {
            // Cause bullet to destroy itself
            Destroy(gameObject);
        }
    }
}

Calculate angles

Next, if we want to fire in a particular direction, we need to do some math. Here’s the code with comments.

 1 // Get the angle of a vector
 2 public float GetYRotFromVec(Vector2 v1)
 3 {
 4     float _r = Mathf.Atan2(v1.y, v1.x);
 5     float _d = (_r / Mathf.PI) * 180;
 6
 7     return _d;
 8 }
 9
10 void Update()
11 {
12     // Get our axis values
13     horizontal = Input.GetAxisRaw("Horizontal");
14     vertical = Input.GetAxisRaw("Vertical");
15
16     // Has the mouse been pressed?
17     if (Input.GetMouseButtonDown(0))
18     {
19         // -- Fire a bullet
20
21         // Create the bullet
22         var bullet = Instantiate(bulletPrefab, body.position, Quaternion.identity);
23         // Get a reference to the bullet's rigid body
24         var bulletbody = bullet.GetComponent<Rigidbody2D>();
25         // Where is the mouse on the screen?
26         var mousePosition = Input.mousePosition;
27         // Where is the mouse in the world?
28         Vector3 target3 = Camera.main.ScreenToWorldPoint(mousePosition);
29         // Set the z value of this vector 3
30         target3.z = 0;
31         // What is the normalized vector from the player to the mouse?
32         Vector2 direction = (target3 - transform.position).normalized;
33         // What is the angle in degrees?
34         float angle = GetYRotFromVec(direction);
35         // Rotate the bullet
36         bulletbody.rotation = angle;
37         // Give the bullet speed
38         bulletbody.velocity = direction * bulletSpeed;
39     }
40 }

Adding a Bloom Effect

Step 1 - Add the post processing package to your project

Go to Window -> Package Manager and then install the “Post Processing” package. This is project-wide so this only needs to happen once for an entire project.

_images/package_manager.png

Step 2 - Enable HDR for the project

_images/enable_hdr.png

Step 3 - Add a post-processing layer to the camera

Select the camera. Add a post process layer component to the camera.

_images/post_process_layer.png

Select the ‘Bloom’ layer. You may need to create this layer if it does not yet exist for your project.

_images/post_process_layer_camera.png

Step 4 - Create a post processing profile

Find/create a directory for post processors.

Create a post processor:

_images/create_pp.png

Add a bloom effect:

_images/bloom.png

Step 5 - Create a post processing volume

Go to your project, add an empty. Call it “post-process bloom” or something like that.

Add a “Process Volume” component to it.

Drag in the post processor to the proper field.

_images/pp_volume.png

This makes everything glow, fine if you are doing some neon geometry wars thing. But what about just one thing?

Step 6 - Make one thing glow

Set post-processing intensity to 1. Zero turns it off, we don’t want that. Above 1 will make everything glow. Don’t want that.

Create a new material called “Glow”.

Give it the following properties:

_images/emission.png

You have to specify the color, it doesn’t pick it up from the image.

_images/glow.png

2D Particle System

Let’s make particles! For a YouTube video that covers this, see: https://www.youtube.com/watch?v=_z68_OoC_0o

Create a white sprite particle

Use Aseprite

Add a particle system

In Unity, select GameObject -> Effects -> Particle System. You should now have a new particle system in your game throwing off fuzzy dots.

_images/particles_1.png

The rotation of the default system has the particles flying up. Take out the -90 rotation on the particle game object and the particles fly towards the camera. Experiment with it.

Experiment with shape of emitter.

_images/shape.png

Add gravity to make the particles fly down.

Make the particles sprites

_images/texture.png

Scale the particles

_images/particle_size.png

Color the particles

_images/color.png

Amount of particles

Adjust “rate over time”

Particle trails

Try adding trails, as shown in the video.

Make things blow up when hit

Update your code so that your bullet script will create a “burst” prefab when you hit an item. You’ll need to have the prefab be created with a script that will destroy itself over time.

Note

This example just shows the important parts. It doesn’t show the needed “make the bullet disappear after a while.” We showed that earlier. You’ll need to combine your scripts.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BurstBulletScript : MonoBehaviour
{
    public GameObject burstPrefab;
    Rigidbody2D body;

    // Start is called before the first frame update
    void Start()
    {
        body = GetComponent<Rigidbody2D>();
    }


    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Destroyable")
        {
            // Destroy the item
            Destroy(collision.gameObject);
            // Create the 'burst' effect
            var burst = Instantiate(burstPrefab, body.position, Quaternion.identity);
        }
    }
}

2D Attacks

This section based off: https://www.youtube.com/watch?v=1QfxdUpVh5I

Add time-limited trigger for attacks

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4
 5 public class CravenAttackScript : MonoBehaviour
 6 {
 7     // How frequently can we attack?
 8     public float attackTimeLimit = 0.5f;
 9
10     // Countdown timer for attacks
11     private float attackCountdownTimer = 0;
12
13     void Update()
14     {
15         // See if we can attack, via timer.
16         if (attackCountdownTimer <= 0)
17         {
18             // We can attack. See if user hit space bar.
19             if (Input.GetKey(KeyCode.Space))
20             {
21                 Debug.Log("Attack");
22                 attackCountdownTimer = attackTimeLimit;
23             }
24         }
25         else
26         {
27             // Attack timer needs count-down
28             attackCountdownTimer -= Time.deltaTime;
29         }
30     }
31 }

Do damage

 1 using System.Collections;
 2 using System.Collections.Generic;
 3 using UnityEngine;
 4
 5 public class CravenAttackScript : MonoBehaviour
 6 {
 7     // How frequently can we attack?
 8     public float attackTimeLimit = 0.5f;
 9
10     // Countdown timer for attacks
11     private float attackCountdownTimer = 0;
12
13     // An empty parented that says where to attack
14     public Transform attackPos;
15     // Radius of attack circle
16     public float attackRange;
17     // What layer will the enemies be on?
18     public LayerMask enemyLayer;
19     // How much damage to deal
20     public int damage = 3;
21
22     void Update()
23     {
24         // See if we can attack, via timer.
25         if (attackCountdownTimer <= 0)
26         {
27             // We can attack. See if user hit space bar.
28             if (Input.GetKey(KeyCode.Space))
29             {
30                 Debug.Log("Attack");
31                 // Reset the countdown timer
32                 attackCountdownTimer = attackTimeLimit;
33                 // What enemies did we hit?
34                 Collider2D[] enemiesToDamage = Physics2D.OverlapCircleAll(attackPos.position, attackRange, enemyLayer);
35                 // Loop through each enemy we hit
36                 for(int i=0; i < enemiesToDamage.Length; i++)
37                 {
38                     // Get the enemy script attached to this object
39                     CravenEnemyScript enemyScript = enemiesToDamage[i].GetComponent<CravenEnemyScript>();
40                     // If there is an enemy script
41                     if (enemyScript)
42                     {
43                         // Damage
44                         enemiesToDamage[i].GetComponent<CravenEnemyScript>().health -= damage;
45                         // Print health levels
46                         Debug.Log(enemiesToDamage[i].GetComponent<CravenEnemyScript>().health);
47
48                         // --- ToDo: destroy enemy here when health <= 0
49                     }
50                     else
51                     {
52                         // We hit an enemy, but there's no script attached to it.
53                         Debug.Log("Enemy Script not present");
54                     }
55                 }
56             }
57         }
58         else
59         {
60             // Attack timer needs count-down
61             attackCountdownTimer -= Time.deltaTime;
62         }
63     }
64     // Used to draw a circle when we are selecting the player in the scene view
65     void OnDrawGizmosSelected()
66     {
67         Gizmos.color = Color.red;
68         Gizmos.DrawWireSphere(attackPos.position, attackRange);
69     }
70 }

Note

You’ll need: * An enemy script * Turn on gizmos in the scene view * An enemy layer * Program a change to the attackPos when user changes direction.

Camel in C#

If you had me for CMSC 150, you likely remember the Camel game. Your task for this assignment is to code the Camel game in C#.

Here is the link for the description of the Camel game:

https://arcade-book.readthedocs.io/en/latest/labs/lab_04_camel/camel.html

Use Visual Studio. It is free to install. You can download it from here: https://visualstudio.microsoft.com/downloads/

Create a new console app project, and call it Camel:

_images/console_app.png

This open with a “Hello World” program. Run the program. It will appear in a separate console window as opposed to a window in the IDE.

Here’s some code to get started:

 1 using System;
 2
 3 namespace Camel
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             // Introductory message
10             Console.WriteLine("Welcome to Camel!");
11
12             // Main game loop
13             bool done = false;
14             while (!done)
15             {
16                 // Print commands
17                 Console.WriteLine();
18                 Console.WriteLine("A. Drink from your canteen.");
19                 Console.WriteLine("B. Ahead moderate speed.");
20                 Console.WriteLine("C. Ahead full speed.");
21                 Console.WriteLine("D. Stop and rest.");
22                 Console.WriteLine("E. Status check.");
23                 Console.WriteLine("Q. Quit.");
24
25                 // Get user command
26                 Console.Write("What is your command? ");
27                 string userCommand = Console.ReadLine();
28                 Console.WriteLine();
29
30                 // Process user command
31                 if (userCommand == "a")
32                 {
33                     Console.WriteLine("You drank from the canteen.");
34                 } else
35                 {
36                     Console.WriteLine("Unknown command.");
37                 }
38             }
39         }
40     }
41 }

Part of this task is practicing how to quickly search up answers. I’m not going to step through how to code in C#, you have enough talent to get started on your own.

We will review some of programs together so we can get ideas from each other.

Today, make sure you have created a project that can print “Hello World.” By the time you come to class Thursday, have a start to the main game loop.

While it is possible to code the program in one function and loop, see if you can use good design and break the parts into functions.

Feel free to change the theme and add features.

If you change the theme, you must still have a number line you are traveling across, some kind of resource you can run out of, and “something” that can catch you.

Be ready to present your work on Thursday and your final project on Tuesday.

To turn in, upload GitHub URL to your project.

Roll-a-Ball

Custom Roll-a-Ball

  • Start with your prior roll-a-ball assignment.

  • Create at least two objects in Blender, and add them as obstacles in your game.

    • Obstacles must be at least as complex as the trees we created.

    • Obstacles must not be the same trees we created. Do something different.

    • Obstacles must have at least two different materials on them.

  • Update the collectable to have a custom shape you create in blender.

    • Feel free to turn off the rotation if it doesn’t work for your collectable.

  • Update your playing field with a custom playing field created in Blender.

  • Grading will be a somewhat subjective. Impress me, don’t shoot for the minimum.

  • Update the image in your read-me.

  • Turn in a Git-Hub link to your project.

Team 3D Game Work

For this project (and the coming weeks) you’ll break into groups and develop a 3D game. Your goal is to improve this game, week-by-week.

Starting the Project

  • Start with one of your roll-a-ball games as a working base.

  • Invite other team members to that project.

  • You must cite any 3d models, textures, sounds that you use from another source.

    • Get this started by creating a section in your readme to hold this info.

  • Figure out how you want to stay in contact. Exchange info. (E-mail, slack, discord, etc.)

  • Brainstorm Ideas

    • Theme?

    • Color scheme? See Adobe Kuler and create a swatch?

    • Create one or more tasks for everyone. Backup tasks are a good idea. See “Some Ideas On Tasks”

Each Week on Thursday

  • Pick goals/tasks for each person to get done that week.

  • Enter each task as an issue in GitHub.

  • Assign it to the proper person.

  • If you notice bugs, enter them in GitHub and assign to the proper person.

Each Week on Tuesday

  • Make sure everyone’s work has been merged into GitHub.

  • Test your application to make sure it is working.

  • Help each other with the tasks and bugs.

Some Ideas on Tasks

  • Object modeling

    • Create an interesting playing field

    • Add more objects, add more detail to objects

  • Materials

    • Learn some of the options for materials, and make things shiny, etc.

    • Note: Learn these in Unity, not Blender. Blender to Unity material transfer is limited.

  • Textures

    • Map an image onto an item (UV Mapping) Can do in Blender or Unity.

    • Learn to use normal maps

    • Create water

  • Shaders

    • Work with shaders to create better looking materials

  • Lighting

    • Instead of one generic light, add better lighting. Spot lights, lamps, etc.

  • Skybox

    • Learn to add a skybox. Warning: Be very careful about how hi-res of an image you download. These can be huge and blow up your project if you download something too big.

  • Sound

    • Add sound for pickups

    • Add sound when bouncing into objects

    • Add sound for movement

  • Create level system

    • Go to new level if all items are picked up

    • Go to new level if player gets to a goal point

  • Enemies

    • Create items that reset the user to the start if you bump into them

    • Create have player lose a life when hitting enemy

    • Support ‘game over’ when player loses all lives

    • Have enemy move towards player

    • Investigate path finding to have enemy move around objects

  • Particles

    • Create liquids, smoke, clouds, flames, magic effects

  • Shooting

    • Be able to shoot things. Enemies, collectables, walls.

  • UI

    • Create intro/instruction screens

    • Allow game restart

    • Show lives left

    • Add background/panel to UI

    • Add dialog system (encounter NPC, have popup dialog)

  • Multiplayer

    • Add networking

  • Animation

    • Animate obstacles

    • Make moving platforms

    • Create switches that trigger events

    • Create a 3d car instead of a ball to move around

    • Create a 3d walking character rather than a rolling ball. Use Mixamo.

  • Player

    • Add ability to jump

    • Add ability to run

Important Notes

  • Do not add assets into a folder without using Unity. This will lead to merge errors that will lose you a lot of time.

  • If working on a challenging item, have a back-up goal. You’ve got to get something done, so you don’t want to be stuck if things are more complex than expected.

  • Everyone must be on the same version of Unity. Do not upgrade your Unity. That will force everyone to upgrade, or you’ll just end up losing your work.

  • Your work must be integrated. For example, if your task is designing a tree, don’t spend all your time making a beautiful tree in Blender and never get it into the game. Create a cylinder in Blender. Get it into the game. Fancy it up with some branches. Get that in the game. Add materials. Get that into the game. If something isn’t in the game, it might as well not exist.

  • Commit early. Commit often. If you only commit during one day this week, it won’t look like you’ve done much work at all.

  • The fancy materials and modifiers you use in Blender are not likely to show up in Unity. Keep it simple. Make sure things work in Unity before sinking a lot of time into them.

Turn In

Turn in a report.

Grading

I’ll grade the way I evaluated the work of my employees back when I worked IT.

  • Integration with the project. When I hit ‘play’ on the game, can I see what you did? If so, that will help give you a good grade. Don’t make the mistake of adding a model, sound, material, or some other component, but not make it part of gameplay. If I hit ‘play’ and can’t see your work, then it serves no purpose. When adding items, start with a simple version. For example, a cube, a beep, code that just prints “hello world” at the right trigger. You have something working. Go back and add detail. Always keep it in the playable game.

  • Frequency of commits. Do you have commits spread across three or more days? This shows ongoing work and integration with the whole project. In the workplace, I’d expect commits every day. Or hour or two. If you are doing something that might break the project, do it in a separate branch, then merge. Ask if you’d like help learning to do this.

  • Quantity/complexity of work. Did you do some scripting? Or add a detailed model? Or add a lot of different low-poly models?

  • Documentation. Did you include links to your project and your commits? Did you detail what you did that works in words? Include screenshots? Did you make it so simple to see what you did, I don’t even need to clone the game? Did you see me in class and show off your work there? Did you use the issue tracking? As a manager, I’m looking at that more than diving into your code. You don’t want managers diving into the code, make it easy for them to track progress.

  • Citations.

2D Assignment 1

In this assignment, we’ll get started with 2D.

Requirements

Turn in a report detailing and showing (with screenshots) your completion of:

Point Allocation

Item

Points

Sprite 1

10

Sprite 2

10

Sprite 3

10

Sprite 4

10

Sprite 5

10

Scene

10

Proper collision

10

Implement scored items

10

Implement next level transition

10

Get something other than player moving

10

Scoring:

  • 0 pts - not implemented

  • 1-5 pts - buggy

  • 6-7 pts - meets minimum requirements. i.e., it works.

  • 8-9 pts - Expanded beyond minimum requirements

  • 10 pts - Expanded into something that looks like an actual game.

Directions

To get started, clone our project. Get invited as a collaborator.

https://github.com/pvcraven/2022_Class_2D_Project

All item names must include yours so we can identify them.

Then most of what you need to get started is at: 2D Unity Part 1.

The main thing not covered is getting some objects to move via scripts. I’m leaving that up to you to figure out.

Sample Items to Create

  • Outdoors

    • Tree

    • Rock

    • Fence

    • Grass

    • Flowers

  • House or some building

  • Icons

    • Pencil

    • Hand

  • Hand-held Items

    • Wand

    • Sword

    • Staff

  • Gems

  • Coin

  • Potion

  • Clothing

    • Boot

    • Shoe

    • Shirt

    • Vest

    • Hat

    • Helmet

  • Food

    • Fruit

      • Pumpkin

      • Apple

      • Pear

      • Orange

      • Grapes

      • Pineapple

      • Raspberry

      • Watermelon

      • Strawberry

      • Cherries

      • Bananas

    • Other food

      • Mushroom

      • Ice cream code

      • Donut

      • Cookie

      • Pizza

2D Assignment 2

In this assignment, we’ll get continue work on our 2D level. This assignment will concentrate on:

  • 2D Tile maps

  • Adding sound effects

Requirements

Turn in a report detailing and showing (with screenshots) your completion of:

Point Allocation

Item

Points

Scoring

Basic Tiles Created

15

Set of 12 background tiles, as shown in class. Must have some detail to get full 15 points.

Additional tiles created

15

Create at least three additional tiles. I’d suggest something that would be an obstruction, or additional background tiles.

Background tile map layer

20

Background tile layer. Must be reasonably extensive for the full 20 points.

Collision tile map layer

15

Add things to run into as part of tile tile map.

Sound effects added

20

Sound effect for points up, points down, and level up/down.

Unity background music

15

Play some background music. Don’t forget to turn it off when changing levels.

Directions

Most of what you need to get started with the tiles can be found in 2D Unity Part 2.

The sound effects and background music should not be too hard. I’m leaving it up to your web-search skills to figure out how to add that.

2D Animation Assignment

Like earlier assignments, please create a doc that points to the work that you did. Get practice documenting and showing off your work, as this is going to be very important for getting promoted in your career.

Point Allocation

Item

Points

Scoring

Create animated time-based sprite frames 1

10

Create at least 8 frames of a time-based sprite. This can be from the fire or water sprite created in class during 2D Animation. To turn in, take a screenshot and show the resulting images.

Create animated time-based sprite in Unity 1

5

Get the animated sprite from above working in Unity. Slow down the keyframes so it doesn’t run full speed.

Create animated time-based sprite frames 2

10

Create at least 8 frames of a time-based sprite.

Create animated time-based sprite in Unity 2

5

Get the sprite working.

Create animated time-based character sprite frames

10

Create “idle” and “walking” animations in Aseprite. Show the images created.

Create animated time-based character sprite in Unity

10

Idle animation must play. When character moves, the walking animation must start. When character is done walking, go back to idle. Select left/right based on direction character is moving.

Create animated time-based NPC sprite frames

10

Create one more moving animation, this time for an NPC. Must have two different animations. (Idle/walk)

Create animated time-based NPC sprite in Unity

10

Get both animations and transition to work for NPC character.

Expand level

10

Take the level that you have, and make it bigger. I suggest showing the original level, and the expansion. You’ll probably want at least two screens worth of additional layout.

Total

100

2D Final Assignment

Like earlier assignments, please create a doc that points to the work that you did. Get practice documenting and showing off your work, as this is going to be very important for getting promoted in your career.

Point Allocation

Item

Points

Scoring

Add the ability to fire projectiles

20

Some kind of projectile needs to be fired.

Create a destroyable target

15

Have an item that is destroyed when hit by the projectile

Create something that glows

15

At least one item needs a glow effect

Create a particle system

20

For full points, the particle effect needs to be triggered. For example, an explosion when an item is hit. Stand-alone particle systems that just emit are worth about 16 points. Scoring will be somewhat dependent on amount of particle features used.

Create an ‘attack’ system.

20

?

Demo your level.

10

Be in class for our last class, and show off your level.

Total

100