Category Archives: Theory

Raycasting Part 1: Everybody Loves Raycasting

raycasting1 Raycasting is one of those topics that everyone seems to struggle with at first, so here is a tutorial covering everything you need to know about raycasting in Unity. In this post I’ll explain what a raycast is, how to use a simple raycast, and point out a few potential problems and pitfalls people run into when using raycasts. More uses of raycasts will be covered in separate posts. A small downloadable demo is included to get you started.

What is a raycast?

A raycast is an ‘imaginary line’ that you can ‘shoot’ through your game scene in order to detect what is in the way of that line (colliders specifically).
  • Raycasts can be either 3D or 2D (you need to use the correct type in order to detect your colliders, as 3D raycasts won’t detect 2D colliders and vice versa).
  • Raycasts can shoot as far as you like, and therefore can detect multiple colliders, or you can just detect the first object in the ray’s sights.
  • Raycasts can be set to use specific physics layers, so you can detect only objects you need to and ignore anything else.

Why would I use a raycast?

Raycasts can be used for all kinds of things, like:
  • Character line of sight (detecting if a character can ‘see’ something)
  • Determining where a missile hit something
  • An alternative to physics collision detection (a lot of platform games replace standard physics with raycasting solutions, which give more control)
  • Input (is an object under a mouse click or touch event).

How do I use a raycast?

It’s actually pretty easy to use a raycast, but it can be tricky to get it right if you’re not careful. We’ll work with 2D raycasts for this demo because it’s easier to demonstrate, but 3D raycasts work exactly the same way, just with an extra axis when determining the direction to shoot the invisible ray. A raycast needs some parameters:
  • an origin from which the ray starts
  • a direction the ray will travel in
  • a distance for the ray to travel (optional – infinity by default)
  • a layermask to determine what objects (colliders) can and can’t be detected by the ray (optional – all layers by default).
Here’s the ‘looking’ code for the demo scene in the sample project you can download below:
[code language=”csharp”] void LookForFood() { var hit = Physics2D.Raycast(eyeLine.position, Vector2.right, 5f, visibleObjects); if (hit && hit.transform.name == "Carrot") { // do something to indicate the rabbit sees a carrot } } [/code]
In this simplified example, the rabbit is looking directly to his right and his sprite changes colour to green if he can see a carrot. The line that does the raycasting is: [code language=”csharp”] var hit = Physics2D.Raycast(eyeLine.position, Vector2.right, 5f, visibleObjects);[/code] This shoots a raycast from the position of the rabbit’s eyeline (eyeline.position), traveling in the direction of Vector2.right (directly to the right of the rabbit’s eyes). The raycast travels for a distance of 5 units (i.e. that’s how far the rabbit can ‘see’ – the raycast will not hit any colliders more than 5 units away from the rabbit’s eyes), and the last parameter is a layermask that determines which objects the rabbit can see. The hit variable will store a RaycastHit2D with information about the collider it detected (it will be null if nothing is hit). In this example the rabbit can see stone or carrots (the food and stone layers are set in the rabbit’s visibleObjects layermask), and only one object is detected by Physics2D.Raycast (you can detect more, but that will be covered in a separate article). Therefore, if a stone is in front of the carrot, the rabbit will only see the stone, but since the rabbit cannot see windows, he will see a carrot behind a window (as long as he’s close enough). Download the sample project to play around. NOTE: To keep the demo simple, I’ve not included any controls. To test the functionality, start the demo, then switch to the Scene window and drag the rabbit object around with the mouse. Keep an eye on the rabbit’s colour to see when he spots a carrot.  

Potential Pitfalls

Parameter Confusion

Because of the variable types used, if you put a layermask in Physics2D.Raycast as the third parameter, Unity will assume it’s the distance you want the raycast to shoot, so you will find your layermask not working! The easy solution is to always specify a distance as well.

Direction vs Target

Many people get confused when putting in the raycast direction. This needs to be the direction the ray shoots, not the destination it shoots towards. If, for example, you put the position of an enemy as the direction vector, the ray would not go towards that position and hit the enemy (unless it just happened to coincidentally be in the right place). Getting the right direction vector can stump beginners, so read up on basic vector maths if you are confused by it. An example you might want to try is checking if any objects are between the player’s eyes and an enemy. To get this direction you would need to subtract the starting vector from the destination vector.

Debug Ray

As mentioned earlier, the raycast is invisible, so you can’t see it! Combine this with the tendency for beginners to have a hard time with vector mathematics and you have a potential for error. Luckily, you can simulate your raycast in the Scene window using the following: [code language=”csharp”]Debug.DrawRay(eyeLine.position, Vector2.right, Color.magenta, 0.1f);[/code] This will draw a short line along the same path as your ray so you can make sure it is firing in the correct direction. The colour parameter determines the colour of the line, and the final parameter is how long you want the line to be visible in the Scene window for before it disappears. Just make sure the first two parameters match your actual raycast exactly! A trick I use is to use a variable to hold a calculation rather than ‘hard coding’ the parameters. That way any change to the calculation will affect both the real ray and the debug ray without the risk of changing one but not the other.

LayerMask Confusion

Double-check your layermask if you use one. If you set your layermask in code, be sure you understand what you’re doing, as layermasks use a counter-intuitive way of setting the layers. You can see more information here.

Conclusion

That’s your introduction to raycasts! Download the demo if you want to try it out or try it out in your own project. If you have any problems, double-check everything in the potential pitfalls listed above.

Artificial Intelligence in Game Opponents

I needed a CPU AI opponent for a simple game, so I decided to build one, and this article details my process. There’s no specific Unity content or project for this article - it’s all theory.

What is Game Artificial Intelligence?

Game artificial intelligence (AI) provides opponents for a human player. Those opponents need to simulate human (or robot, or animal) behaviour to provide a challenge. A player wants opponents that act and react as a human opponent would. This is difficult because humans are organic, and computers are digital. And of course, computers don’t think for themselves like people can. Game AI therefore needs to use purely logical algorithms to simulate the problem solving that a human player does organically. You need to make those algorithms feel natural.

A Simple Multiplayer Game

The game I’m creating AI for is a very simple card memory game, the kind where you have a grid of face-down cards, and you try to turn over two matching cards to win a point: matching_cardsThis game hardly requires a super-intelligent robot mind, but I found it was a great way to teach myself the fundamentals of game AI.

Define the Rules

To start with you need to know your game’s rules. Memory card games have very simple gameplay: Players take turns to:
  • Turn over two cards, one at a time (making them face-up and visible to both players)
    • If the cards match, they are removed from play and the player gets a point and another turn
    • If the cards don’t match, they are turned back face down and the other player has their turn.
The player with the most points when all the cards are gone wins.

Implementing AI

I chose to implement a very rough AI to start with. AI version 1.0 picks cards completely at random. As you can guess, this is not challenging, and it doesn’t fit any definition of AI. But it did let me set up the programming infrastructure in my game. My standard approach to adding functionality is to add in the skeleton first - typically the way the new functionality interacts with the rest of the code. Doing this lets me work on the new feature in isolation knowing that changes to it won’t affect the rest of my game (so long as I keep a simple interface between the main game and the new functionality). I created a class called AiOpponent containing a single public method ‘ChooseCard()’. ChooseCard() returns a randomly selected card from the current game board. The core game knows that the player is AI and therefore must be asked to select a card rather than wait for a human player to tap the screen. The rest of the game continues as normal. Once this was working I could encapsulate all my AI code into the AiOpponent class, but never have to change any other code in the game. The game manager doesn’t care how a card is selected, just that one is.

From Zero to Hero

I played the game a few times to figure out the main strategy. The core strategy is to remember the cards that are turned face-up and then returned face-down. The perfect strategy was to remember every card and then use that information to improve the chance of making pairs. I decided that version 2.0 of my AI would play a ‘perfect game’ and remember every single card with 100% accuracy (this is actually simpler than implementing an imperfect strategy), and always make a pair when possible (i.e. when it knows where two matching cards are or when it knows where a card matching its first selection is). I broke this down into the following flowchart: ai_flow

Considerations for v2.0

As you can probably guess, this AI is almost unbeatable. It doesn’t make mistakes, and it has a perfect memory. Unless you get lucky and also have a perfect memory you will usually lose. Like a random opponent, a perfect opponent isn’t any fun. AI v2.0 doesn’t forget a card they saw several turns ago; it doesn’t accidentally pick up the card next to the one it intended. AI v2.0 is too machine-like; too perfect.

AI Overkill

I’m the first to admit that the AI for my card memory game is overkill. It’s a simple kids’ game, and having a purely random opponent would work just fine. But I wanted to teach myself how to create an AI opponent so I can apply that knowledge to a more complex game I plan to make later. So I set out to make the most human card memory AI I could.

v3.0

The next step was to make the AI adaptable to different difficulty levels by blending the random card selection from v1.0 and the perfect matching skills of v2.0. Based on the difficulty level my AI would choose randomly or perfectly in different proportions.
  • Easy AI - totally random
  • Medium AI - half random, half perfect
  • Hard AI - totally perfect.
v3.0 is probably the most complex AI you could possibly need for this kind of game. In fact, it’s probably already overkill. But once I’d started deconstructing the game I realised even this simple game has more potential for AI, so in a future post I will go into the nuances of a more human card memory opponent (even though it’s overkill).