This week we looked some game designs from our ninjas. We ended with four:
Duty to Defend – A tank-based flag-defence game played in first-person.
Monster POV – Play as a monster defending a dungeon against hero characters. Isometric viewpoint.
Red Panda’s Shopping – An exploration and collection game with puzzle elements.
Gardening Simulator – Grow and harvest plants in a top-down game to earn money and unlock new seeds and areas
A vote was held and the winner was Monster POV!
We’ve asked ninjas this week to find some visual references for the look-and-feel of the game (given that it’ll be low-poly) and also to consider some lore or story that would enhance the game and inform the creation process.
We started off this year with the Spirals Project.
We could specify how many lines the code should draw, how many sides the Spiral should have and then finally how many extra degrees we should turn before drawing the next line.
I have uploaded 2 versions of this project to the Scratch Website, you can get all the code and also play with them online.
ClassVersion-Spirals – This is the one we ended up with where it creates the early universe. ClassVersion-SingleSpiral – This is the one that we started with, where you can choose the different values.
to document your game idea. Remember that we don’t need a timeline, just the game concept. These files are just templates, so feel free to add anything you thinks is important and to ignore or delete anything you don’t think is important.
You will need to be connected to Microsoft Teams to download these files. Once you’re successfully connected to Teams, remember to say “Hello” in the 2023 Say Hello channel.
Also try to have Blender installed before next weeks session. This will avoid problems with passwords/IT locks/etc. Blender can be downloaded here.
CoderDojo Athenry will be returning on Saturday 23 Sept 2023 with an information session. Our first normal session will be the following Saturday, 30 Sept 2023.
The information session will be a short session, running from 12-1pm. All subsequent sessions will be two hours in duration, from 12-2pm.
If you have not attended CoderDojo Athenry before, please do your best to attend the information session. For all others it is optional.
We have four groups running this year:
EXPLORERS: for young kids (must know how to read) Learn Scratch! Program your own interactive stories, games, and animations. Scratch helps kids to learn to think creatively and reason systematically.
ADVANCERS: for kids ready to move on from Explorers, features more advanced projects in Scratch and some other technologies.
BODGERS: for kids who have done Scratch and want to move on to a more advanced coding language. We’ll use Python to create our own animations and games similar those being created in both Scratch groups. This year we may also look at using Python to control some electronic hardware.
CREATORS: an introduction to game design and programming using the Unity Game Engine and Blender 3D modelling software.
Ninjas from 7 to 17 are welcome to join us. Children aged 12 and under must be accompanied by a parent/guardian who stays in the same room as them for the full session.
Hi Creators, the final version of Gem Search is uploaded to our GitHub. The HighScore functionality is completed. The InitialiseInventory component has been disabled, but the word “Inventory” on the inventory screen is now a secret button you can use to get some gems at run-time.
Updated 22/05: Credits added.
Looking forward to seeing you all at the party on next Saturday! Thanks for a fun year.
This week we took the completed game and started to look to making it more fun to play and to iron out some of the things that don’t work well at present.
Distance Measured to Buried Gem
The original gem sensor measured the distance between itself and the nearest gem and used this for signal strength. This didn’t always work well, especially on down slopes. This diagram shows why:
The straight-line distance to the gem (blue lines) on a downslope is far greater that it would be on the flat.
The solution to this was to compare just the horizontal distance between sensor and the gem, removing any height differences.
We had to make this change in both ProximitySensor.cs and PlayerDig.cs.
Sensor Signal Strength
The signal strength was calculated as follows:
Signal Strength = Distance related factor * Direction related factor
The first attempt we made at signal strength was purely based on distance, in practice this proved to be super hard to use. Including direction made it a lot easier to find the gems, but the fact that the distance related factor dropped to zero, hence making the signal strength zero, the instance our gem was behind us, even the tiniest amount, didn’t feel correct.
Our adaption was to make it so that the Signal Strength has a strong component that ‘s directional, but a small independent component that’s distance based. Here’s what that looks like:
Signal Strength = Distance related factor (0.2f + (0.8f * Direction related factor)
If we look at this equation, even when the direction related factor is zero, the signal strength doesn’t drop completely to zero, although it does get a lot lower. This feels a lot better.
Tweaking and Polishing
Our final few weeks will consist of tweaking and polishing the game. We have a document here:
Imported an asset pack containing our new Inventory UI and a prefab to serve as an inventory slot
Worked on the code for the inventory slot
Updated Gem Images
We downloaded GemImages.zip from our Teams site. Importing these files directly into Unity wouldn’t work. Unity associates an identifier (which is a large random number) with assets in a project. This identifier is commonly called a GUID or Globally Unique Identifier. This allows us to rename and move things and any links we’ve established remain in place.
GUIDs aren’t absolutely guaranteed to be unique, but the chances of two being the same is 1 in 360 undecillion. What’s an undecillion? It’s a 1 followed by 36 zeros. Not just huge, but unbelievably vast.
If we import new assets, they’ll get new GUIDs. Anything we linked to the old versions will remained linked to the old versions. What can we do?
Well we can get around this by updating the files outside of Unity. If we replace the files in Windows Explorer or Mac Finder, then Unity sees they’ve updated, but considers them still the same asset they were before and all links remain good.
You can see, when looking in Windows Explorer or Mac Finder that for every file and folder in a Unity project there is also a file of the same name but with the additional ending .meta. This file stores all the asset properties from the Inspector window and, surprise surprise, the GUIDs as well.
New UI Asset
We downloaded the GemInventoryUI.assetpack from our Teams site and imported it into Unity. There were warned that we were going to overwrite existing assets and change their GUIDs. This happened because we’d previously imported frame3_distort.png and frame3_distort_hollow.png, which are in this asset pack as well. Every single one of us ended up with a different GUID for these assets.
When this happens you have two choices:
Import these assets from the asset pack and break any links that exist in your project already
Don’t import these assets from the asset pack and break any links with items within the asset pack
In this case, the choice is easy. We only used frame3_distort.png once in our project and we haven’t used frame3_distort_hollow.png yet at all. In the asset pack, in contrast, we’ve used these two assets several times. We choose the first option and we just have to edit our Gem Found prefab to fix the link to frame3_distort_hollow.png in the Panel control that’s now broken.
We also changed the colour of all the “Header” text in our new Inventory Canvas prefab from Palatinate Blue to Cherry Blossom Pink. This is a nicer balance of colour and retains the strong Palatinate Blue colour for the button, emphasising that it’s an action.
Finally, we dragged a copy of this Inventory Canvas prefab into our Main Scene. It can’t be used right now, because the First Person Controller has the mouse locked to itself to control the look direction, so we just disabled it for the time being.
Inventory Slot – Starting to Work on the Prefab
To work on the Inventory Slot we created a new scene called Inventory Slot Test. We set it up as follows:
Added a Canvas
Added a Panel to the canvas. It’s automatically set to stretch to it’s parent, filling the canvas.
Set the Panel’s background to anything other than white and set it to opaque (A = 255)
Add another Panel as a child of the Panel above. Call it “Slot Holder”
Change Slot Holder’s background to white and opaque.
Changed Slot Holder’s Anchor and Pivot: Click on the button in the Rect Transform component. While holding the SHIFT and ALT keys, select top-left.
Now that the anchor’s changed, the size and position can be set. Set the size to 100×100 and move Slot Holder so it’s somewhere near the middle of the left-hand side of the screen.
Duplicate Slot Holder and move the copy to the middle of the right-hand side of the screen.
Drag a copy of the Inventory Slot component as a child of both Slot Holder and Slot Holder (1)
Your screen should look similar to this when done:
We create a new script called InventorySlot.cs in our Scripts folder. Edit the Inventory Slot prefab and add the script to the prefab.
Note that it’s easy to make these changes to the copy of the prefab in the scene, but that’s not what we want. We want to edit the prefab itself so that every copy of the prefab, including those in the Inventory Canvas, get the changes.
Updating GemDefinition.cs
We take a little detour here to update GemDefinition.cs. We need a Sprite so we can attach it to an image control, etc. Gem Definition has a Texture2D. Similar but not the same. We can make it so that Gem Definition makes a sprite from this Texture2D the first time we ask for it, and holds onto that Sprite anytime we ask for it again. Here’s the updated code:
[CreateAssetMenu(menuName = "Gem Search/Gem Definition")]
public class GemDefinition : ScriptableObject
{
public int Level;
public int Value;
public GameObject Prefab;
public Texture2D Icon;
private Sprite _sprite;
public Sprite Sprite
{
get
{
if (_sprite == null)
{
_sprite = Sprite.Create(Icon, new Rect(0, 0, Icon.width, Icon.height), new Vector2(0.5f, 0.5f));
}
return _sprite;
}
}
}
The property Sprite is not like any we’ve seen before. It actually has code that runs when we access it under get. We could also have code that runs when it’s set (under set unsurprisingly). Since we’ve specified get here and not set this property is read-only; that’s often a useful thing.
Since this new-style property can’t actually store anything on it’s own, there’s a private property called _sprite that actually does the storing of the value. When someone asks for GemDefinition.Sprite, it checks to see if it’s been made before. If it has, it just returns it. If it hasn’t it makes it and then returns it.
Inventory Slot – Showing the Gem and the Count
Now that we have a Sprite we can use, let’s start plugging this together. Here’s the code for InventorySlot.cs:
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class InventorySlot : MonoBehaviour
{
public GemDefinition GemDefinition;
public int GemCount;
public Image SlotImage;
public GameObject CountTextBadge;
public TMP_Text CountText;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (GemDefinition == null || GemCount < 1)
{
SlotImage.sprite = null;
}
else
{
SlotImage.sprite = GemDefinition.Sprite;
}
CountTextBadge.SetActive(GemDefinition != null && GemCount > 1);
CountText.text = GemCount.ToString();
}
}
We edited the Inventory Slot prefab and assigned the SlotImage, CountTextBadge and CountText to the corresponding items in the prefab (Content Image, Item Count Badge and Count Text respectively).
We then ran our scene. We can see that when we assign GemDefinition to the slot and set the count to 1 or higher, the image and the count badge behave as expected.
Inventory Slot – Adding Interfaces for Drag and Drop
We need Unity to send messages to our InventorySlot component when dragging and dropping is occurring, but how to we do that? Well, if our component implements certain Interfaces then it says to Unity, I’m interested in Drag and Drop.
An Interface in C# is just a contract that a class will have certain properties and functions. To things outside the class it means they can now access the class through these properties and functions, confident that they exist. It’s up to the class itself to actually make sense out of the properties and functions so that something appropriate happens!
First we need to add a new using to InventorySlot.cs. This is where the Interfaces we’re interested in are defined:
using UnityEngine.EventSystems;
Now we update the class definition to include the Interfaces we want to support:
public class InventorySlot : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler, IDropHandler
If you are using Visual Studio, you should find that these Interfaces each have a wavy red underline because their contract is not fulfilled. You should be able to right-click on each one, choose “Quick Actions and Refactorings” and “Implement Interface” to get it to fill in the interfaces for you automatically. You will see four new functions, each with a throw line that makes an error happen if the function is called. This is to force you to put your own code in. Just delete these throw lines are you’re left with:
public void OnBeginDrag(PointerEventData eventData)
{
}
public void OnDrag(PointerEventData eventData)
{
}
public void OnDrop(PointerEventData eventData)
{
}
public void OnEndDrag(PointerEventData eventData)
{
}
Inventory Slot – Begin Dragging
To allow dragging, we want to create a new image on the canvas and move it with the mouse. First we add these two private properties to InventorySlot.cs:
public void OnBeginDrag(PointerEventData eventData)
{
if (GemDefinition == null || GemCount < 1)
return;
_draggingIcon = new GameObject("Icon");
_draggingIcon.transform.SetParent(_canvas.transform, false);
_draggingIcon.transform.SetAsLastSibling();
var image = _draggingIcon.AddComponent<Image>();
image.sprite = GemDefinition.Sprite;
OnDrag(eventData);
}
public void OnDrag(PointerEventData eventData)
{
if (_draggingIcon == null)
return;
RectTransform iconRt = _draggingIcon.GetComponent<RectTransform>();
RectTransform canvasRt = _canvas.GetComponent<RectTransform>();
Vector3 globalMousePos;
if (RectTransformUtility.ScreenPointToWorldPointInRectangle(canvasRt,
eventData.position,
eventData.pressEventCamera,
out globalMousePos))
{
iconRt.position = globalMousePos;
}
}
Now when we run and set a GemDefinition and Count of one or higher on our slot, if we drag from that slot with the mouse, we get an icon that shows the gem and moves with the mouse. Once we stop dragging it remains in place.
This is most of drag and drop done; we just need to make a few tweaks next session: control the size of the drag icon, make it semi transparent and make sure it doesn’t block the mouse interactions and remove it up when we stop dragging.
Code Download
The code for this week’s project is on our GitHub, as always.
Hi everyone,
We completed our Mario game this week. We coded Mario so that he always floated down on to the wall. We added a fraction of a second of a wait so that it appears that he floats as he comes down. This also allows time for you to navigate left or right as needed.
We also introduced a more advanced concept, the Parallax effect, whereby objects further away appear to move slower than objects nearer. We coded mountains and a Sun to demonstrate this.