"How real games do it" is just such a good series! It is really frustrating to see unusable tutorials because they are too "gamejamy"... Thanks and keep it up!
So what it means : (1 00000000000000000000000000010000 (1 00000000000000000000000000000000 There is no 1 in common => There is no common layer. Ex: With layer 5: 00000000000000000000000000010000 & 10000000000000000000000000110010 => 00000000000000000000000000010000 There is a 1 in common => There is at least one common layer.
Dude, incredible comment! I've been trying to wrap my brain around bitwise operators for YEARS, this finally got into my thick skull! Thanks for an amazing explanation, and thanks to Brandon for honestly admitting he didn't totally understand the concept and asked for an explanation!
OverlapCircleAll() takes a layerMask parameter so I think you can simplify that logic to only see if you get any results from the call instead of iterating through them and comparing the layers.
Thank you for your excellent Unity tutorials! 👏 Bitwise operators and shifting comes from the manipulation of "bits" that are 0s and 1s which are used to "store" the Integerns in Computer Memory. For example: - Integer 0 is 0 in binary and is stored as 0000 0000 in an 8 bit system or 0000 0000 0000 0000 in a 16 bit system. - Integer 1 is 2^0 and is stored as 0000 0001 in an 8 bit system or 0000 0000 0000 0001 in a 16 bit system. - Integer 2 is 2^1 and is stored as 0000 0010 in an 8 bit system or 0000 0000 0000 0010 in a 16 bit system. - Integer 3 is (2 + 1) which is (2^1 + 2^0) so there are 1s at position 1 and 0 -> stored as 0000 0011 in an 8 bit system or 0000 0000 0000 0011 in a 16 bit system... and so on. Shifting refers to operations that the CPU is able to perform directly on these "bits", that is shift a 1 left (). Also we can execute logical operators on these "bits" to quickly get a True or False result. e.g. 1 & 2 = 0 i.e. False (because the bits in these don't align) 1 & 3 = 1 i.e True (because the bits in the position 1 align) 2 & 3 = 2 i.e True (because the bits in the position 2 align) These operations are extremely fast to do on bits vs Bools or Ints. This article gives a good intro to the concept: www.programiz.com/c-programming/bitwise-operators LayerMasks uses this representation of Integers to define "layers" each mapped to a specific position in the 32 bit representation of an Integer (which is why there are 32 different layers in Unity :) This article given a good intro to layer masks in Unity: mathiassoeholm.github.io/Layer-Masks-In-Depth/
So to help me understand bitwise operators, in this case Bitwise & operator. I always imagine as it loops through each bit and it does if statmenet on each one of them to see if it is positive or not. For example: (5 & 3) ..8421 5 in bits is : 00000101 3 in bits is : 00000011 As you see numbers above add up to the value. so if you do 5 & 3 you are doing basically 00000101 & 00000011 so we go through each bit from right to left and do the if statement 1 && 1 is TRUE (1) 0 && 1 is FALSE (0) 1 && 0 is FALSE (0) rest values are 0s 00000101 & 00000011 = 00000001 So 5 & 3 is equal to 1. Try Debug.log(5 & 3); I would try few examples and see if you can predict value you will get. Eventually you will get hang of it.
Ah, this reminded me of the old debate regarding asking for permission vs beg for forgiveness, loved it! #Respect for taking the "problem solver" approach instead of the "coder" one. Bitwise operations will make more sense if you first look into binary operations and numeric base switching. That being said, you have there 2 operation, first one is a left shift and the second one is a logical and. First things first, computers know only about binary, so how do you tell a computer you want the number '10' for example represented in memory? You switch it from the base you are used to (base 10) to the base that the computer understands (base 2). E.G.: Base conversion: The number '10' in decimal is '1010'. Consider the binary notation as a power of 2 representation of the number: 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 = 1 * 8 + 0 * 4 + 1 * 2 + 0 * 1 = 8 + 2 = 10 Shifting (left): The values of the bits of your number in binary representation get moved (shifted) towards a direction: 10
Thanks for your spent precious time on helping people like me. ❤❤❤ Wish you all the best. You are the best MAN! I dont even know how to appreciate you, besides subscribing and liking. Hello from Tajikistan. Now waiting for How do a AAA companies make games.😊
the short explain, you using flags with enum. when you set it up, its allow you to choose multiple items in the Enum, while the first one (enum = 0) will be "none" the idea is that in memory if set the enum as flags you can set up to 32 items without over lapping in memory. so in your "if" check you checking if the memory is 1 or 0 , and than you can check all the items for it and have multiple chooice or option. also anther option than just using it in layer is making an enum and putting " [System.Flags] " on top of it, and than in unity editor you will get the same Enum toggle list like in the camera layers. tried to keep it as simple explanation.
I am not sure how well you understand binary so I am going to explain this as detailed as I can. This is going to be long. As you are probably aware computers store everything as a binary number. So if you have an enum for collisions with values PLAYER=0, ENEMY=1, WALL=2, GROUND = 3 if you set an int to those values it will store it internally as 00, 01, 10, 11. Now lets say the player is colliding with both the ground and an enemy at the same time? What value should we return from our collision function? 01 or 11? Maybe a list with both values? Yeah that would work but iterating over that list is costly in time, plus it also cost space to return 2 ints. We can do better. We are allowed setup and interpret the bits anyway we want. So lets use bit values instead, remember these are binary values I am assigning to the enum, PLAYER = 0001, ENEMY = 0010, WALL = 0100, GROUND = 1000. This has the advantage where now we can return all collisions with one int. If the player is colliding with just an enemy we can return 0010, if the player collided with just the ground we return 1000. If they collided with both return 1010! So lets say you collided with both and the function returns 1010. How do you extract that data? That is where the bitwise operator &(pronounced "and") is used. This goes bit by bit and compares the ones and zeroes. If both bits are 1, the output is set to 1 at that bit. Otherwise the output at that bit is set to 0. So if I do something like (WALL & collisionOutput) it will do the following math 0100 //value of WALL enum & 1010 // collisionOutput 0000 //result of & the two values As you can see the result is 0. None of the bits of the two numbers are both one so all bits are 0. In code 0 is false when checked in an if() statement. Which is what we expect, the player did not collide with a wall. Lets check enemies next (ENEMY & collisionOutput) 0010 //value of ENEMY enum & 1010 // collisionOutput 0010 //result of & the two values The result of the & is 0010. Only the second bit of the two numbers is one in both numbers. When you call an if() statement on any number other then 0 it is interpreted as true. So this will return true, your code now knows it collided with an enemy! You can do the same thing for GROUND and see that it works as well. We are almost there, but we have a problem. Your enum isn't setup like mine where every value corresponds to a bit. Yours is just consecutive numbers 0,1,2,3. Well that is where the bit shift operator
One way I've done it that avoids getting a random number and checking if it's correct is to get an array of all possible points in a grid, shuffle it and take the first n points, this way you don't have to keep trying in case RNGesus is angry at you.
nice, im glad to see that my method of handling this problem was the right one, and bitwise operations were confusing only experimenting with it made me understand, just plain text hard to understand p.s. in my case that was an airdrop, i wanted the airplane to avoid dropping it above buildings, trees and rocks, so it always land on valid terrain for players to reach it.
The stuff withh layerMask you do not understand is not really hard to realize. Its just a way of storing N boolean variables in single int variable. Its used for performance reasons, as 64bit int is a processor word size while array is more complex object that has its performance implications. Think your int not as a classic number but as an array of 64 booleans, with every bit stored into. Lets say you have 4 bit number and you have this bit flag to determine if enemy is vulnerable agains one of 4 elements: fire, air, earth and water. So instead of storing it as boolean[4], you can store it in single int and treats bits of these ints as flags. An enemy vulnerable to fire would have binary value of 0001, to air it will be 0010, earh 0100 and so on. If your enemy is vulnerable agains fire and earth, it would be like that: 0101. But in the code (and inspectors and stuff) you are seeing this as simple int value, so enemy vuln to fire could have value of "1", enemy vuln to air - "2", enemy vuln to earth "4" and enemy vuln to water "8" (play around with some online bit to int conversions, you will quickly get how it goes). Enemy vuln to fire and earth could have a value 0101 which is 5 (1+4). If you want to add air resistance (binary 10 === 2) you could add 2 to this value, so 5+2 = 7 and 7 represents vunlerability to fire, earth and air. The thing is, doing this is not safe, because it does not check if the flag already exists on it, adding air vuln to vlaue of 7 would make int to end with a value of 9 which is resistance to fire and earth. But we have bitwise operators that are safer to do, in these examples I assume vulnerability to earth is 2: 1) adding earth vuln to list is: vulnerabilites = vulnerabilities | 2; (binary OR) 2) removing earth vuln from list: vulnerabilites = vulnerabilities & (~2); (binary AND NOT) 3) checeking if enemy is vulnerable: if ((vulnerabilities & 2) == 2) { damage*=2; }; (binary AND + equeals) So whats about the "
bro i have some advice. i think you should cut down to 1 video per week instead of 2, more time for your game and avoiding youtube burnout and more focus one video
hmmm, I went with a different approach, I used template* for enemies spawn point and utilities(inner walls and items), so there is like 12 template of spawning (at least for now), and it just enemy position, and when we add it with the template of inner walls, and item box, which is 20 variation, I got 240 variation of rooms. I got the inspiration from a android game called soul knight.
Regarding the bitwise operation: a byte is 8 bits, a word is 2 bytes, a long-word is 4 bytes, etc. Lets just use one byte as an example The decimal value 1 would be 00000001 in binary. If you shift that left 1 bit you get 00000010 which is 2 decimal. If you shift it left again you get 00000100 which is 4 decimal. etc. Make sense?
You're not explaining why it's shifting or what shifting is really doing. You're just saying "if I push it, it moves", you're not explaining why it moves or how to get to move more or less space.
Think of it as a mathematical operation like addition or multiplication. This is no different then saying "if I multiply a number by 10 it 'moves' one place to the left (e.g. from the 10s place to the 100s place)." In the same way, if you shift a value one bit to the left, the result is that the value is moved one place to the left. The only difference is that it's base 2 not base 10.
Depends on your needs. I would say a pooler is better for spawning in many of the same type of object, but not really for spawning in different kinds. A pooler normally wants to know how many will spawn at a time and which types and isn't very good with randomization. At least, not in my experience.
@@Dyanosis I do not disagree. On the other hand, I have made and seen code that allows for you to incorporate all types of many objects in an object pooler. It is far more difficult, but it is still possible. Heck, in the weirdest of situations, I have even pooled a pooler because of random data leak that could not be fixed with any other approach.
"How real games do it" is just such a good series!
It is really frustrating to see unusable tutorials because they are too "gamejamy"...
Thanks and keep it up!
So what it means : (1 00000000000000000000000000010000
(1 00000000000000000000000000000000
There is no 1 in common => There is no common layer.
Ex: With layer 5:
00000000000000000000000000010000
& 10000000000000000000000000110010
=> 00000000000000000000000000010000
There is a 1 in common => There is at least one common layer.
🔥 explanation sir!
I still don't understand 😢 i think I'm just dumb 😅
I have finally understood! thank you so much!@@junaidywijaya
Dude, incredible comment! I've been trying to wrap my brain around bitwise operators for YEARS, this finally got into my thick skull! Thanks for an amazing explanation, and thanks to Brandon for honestly admitting he didn't totally understand the concept and asked for an explanation!
Very clearly & well understanding sir, ty so much!
OverlapCircleAll() takes a layerMask parameter so I think you can simplify that logic to only see if you get any results from the call instead of iterating through them and comparing the layers.
Thank you for your excellent Unity tutorials! 👏
Bitwise operators and shifting comes from the manipulation of "bits" that are 0s and 1s which are used to "store" the Integerns in Computer Memory. For example:
- Integer 0 is 0 in binary and is stored as 0000 0000 in an 8 bit system or 0000 0000 0000 0000 in a 16 bit system.
- Integer 1 is 2^0 and is stored as 0000 0001 in an 8 bit system or 0000 0000 0000 0001 in a 16 bit system.
- Integer 2 is 2^1 and is stored as 0000 0010 in an 8 bit system or 0000 0000 0000 0010 in a 16 bit system.
- Integer 3 is (2 + 1) which is (2^1 + 2^0) so there are 1s at position 1 and 0 -> stored as 0000 0011 in an 8 bit system or 0000 0000 0000 0011 in a 16 bit system... and so on.
Shifting refers to operations that the CPU is able to perform directly on these "bits", that is shift a 1 left (). Also we can execute logical operators on these "bits" to quickly get a True or False result. e.g.
1 & 2 = 0 i.e. False (because the bits in these don't align)
1 & 3 = 1 i.e True (because the bits in the position 1 align)
2 & 3 = 2 i.e True (because the bits in the position 2 align)
These operations are extremely fast to do on bits vs Bools or Ints.
This article gives a good intro to the concept: www.programiz.com/c-programming/bitwise-operators
LayerMasks uses this representation of Integers to define "layers" each mapped to a specific position in the 32 bit representation of an Integer (which is why there are 32 different layers in Unity :)
This article given a good intro to layer masks in Unity: mathiassoeholm.github.io/Layer-Masks-In-Depth/
So to help me understand bitwise operators, in this case Bitwise & operator. I always imagine as it loops through each bit and it does if statmenet on each one of them to see if it is positive or not.
For example: (5 & 3)
..8421
5 in bits is : 00000101
3 in bits is : 00000011
As you see numbers above add up to the value.
so if you do
5 & 3 you are doing basically
00000101 & 00000011
so we go through each bit from right to left and do the if statement
1 && 1 is TRUE (1)
0 && 1 is FALSE (0)
1 && 0 is FALSE (0)
rest values are 0s
00000101 & 00000011 = 00000001
So 5 & 3 is equal to 1.
Try Debug.log(5 & 3);
I would try few examples and see if you can predict value you will get. Eventually you will get hang of it.
Thanks so much!!
Ah, this reminded me of the old debate regarding asking for permission vs beg for forgiveness, loved it!
#Respect for taking the "problem solver" approach instead of the "coder" one.
Bitwise operations will make more sense if you first look into binary operations and numeric base switching. That being said, you have there 2 operation, first one is a left shift and the second one is a logical and. First things first, computers know only about binary, so how do you tell a computer you want the number '10' for example represented in memory? You switch it from the base you are used to (base 10) to the base that the computer understands (base 2).
E.G.:
Base conversion: The number '10' in decimal is '1010'. Consider the binary notation as a power of 2 representation of the number:
1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 =
1 * 8 + 0 * 4 + 1 * 2 + 0 * 1 =
8 + 2 =
10
Shifting (left): The values of the bits of your number in binary representation get moved (shifted) towards a direction:
10
Thanks for your spent precious time on helping people like me. ❤❤❤ Wish you all the best. You are the best MAN! I dont even know how to appreciate you, besides subscribing and liking. Hello from Tajikistan. Now waiting for How do a AAA companies make games.😊
the short explain, you using flags with enum.
when you set it up, its allow you to choose multiple items in the Enum, while the first one (enum = 0) will be "none"
the idea is that in memory if set the enum as flags you can set up to 32 items without over lapping in memory.
so in your "if" check you checking if the memory is 1 or 0 , and than you can check all the items for it and have multiple chooice or option.
also anther option than just using it in layer is making an enum and putting " [System.Flags] " on top of it, and than in unity editor you will get the same Enum toggle list like in the camera layers.
tried to keep it as simple explanation.
Had no idea about the System.Flags, thanks!!
I am not sure how well you understand binary so I am going to explain this as detailed as I can. This is going to be long.
As you are probably aware computers store everything as a binary number. So if you have an enum for collisions with values PLAYER=0, ENEMY=1, WALL=2, GROUND = 3 if you set an int to those values it will store it internally as 00, 01, 10, 11.
Now lets say the player is colliding with both the ground and an enemy at the same time? What value should we return from our collision function? 01 or 11? Maybe a list with both values? Yeah that would work but iterating over that list is costly in time, plus it also cost space to return 2 ints. We can do better.
We are allowed setup and interpret the bits anyway we want. So lets use bit values instead, remember these are binary values I am assigning to the enum, PLAYER = 0001, ENEMY = 0010, WALL = 0100, GROUND = 1000. This has the advantage where now we can return all collisions with one int. If the player is colliding with just an enemy we can return 0010, if the player collided with just the ground we return 1000. If they collided with both return 1010!
So lets say you collided with both and the function returns 1010. How do you extract that data? That is where the bitwise operator &(pronounced "and") is used. This goes bit by bit and compares the ones and zeroes. If both bits are 1, the output is set to 1 at that bit. Otherwise the output at that bit is set to 0.
So if I do something like (WALL & collisionOutput) it will do the following math
0100 //value of WALL enum
& 1010 // collisionOutput
0000 //result of & the two values
As you can see the result is 0. None of the bits of the two numbers are both one so all bits are 0. In code 0 is false when checked in an if() statement. Which is what we expect, the player did not collide with a wall.
Lets check enemies next (ENEMY & collisionOutput)
0010 //value of ENEMY enum
& 1010 // collisionOutput
0010 //result of & the two values
The result of the & is 0010. Only the second bit of the two numbers is one in both numbers. When you call an if() statement on any number other then 0 it is interpreted as true. So this will return true, your code now knows it collided with an enemy! You can do the same thing for GROUND and see that it works as well.
We are almost there, but we have a problem. Your enum isn't setup like mine where every value corresponds to a bit. Yours is just consecutive numbers 0,1,2,3. Well that is where the bit shift operator
Whoa. Going to have to read this a couple times. Thanks!!
Wow! This was helpful! Thank you so much
Thanks for the tutorial :D I loved it, keep more coming nwn
One way I've done it that avoids getting a random number and checking if it's correct is to get an array of all possible points in a grid, shuffle it and take the first n points, this way you don't have to keep trying in case RNGesus is angry at you.
Another great video.
Great series! Have a subscribe!
Kudos for showing the offset option
nice, im glad to see that my method of handling this problem was the right one, and bitwise operations were confusing only experimenting with it made me understand, just plain text hard to understand
p.s. in my case that was an airdrop, i wanted the airplane to avoid dropping it above buildings, trees and rocks, so it always land on valid terrain for players to reach it.
The stuff withh layerMask you do not understand is not really hard to realize. Its just a way of storing N boolean variables in single int variable. Its used for performance reasons, as 64bit int is a processor word size while array is more complex object that has its performance implications. Think your int not as a classic number but as an array of 64 booleans, with every bit stored into. Lets say you have 4 bit number and you have this bit flag to determine if enemy is vulnerable agains one of 4 elements: fire, air, earth and water. So instead of storing it as boolean[4], you can store it in single int and treats bits of these ints as flags. An enemy vulnerable to fire would have binary value of 0001, to air it will be 0010, earh 0100 and so on. If your enemy is vulnerable agains fire and earth, it would be like that: 0101.
But in the code (and inspectors and stuff) you are seeing this as simple int value, so enemy vuln to fire could have value of "1", enemy vuln to air - "2", enemy vuln to earth "4" and enemy vuln to water "8" (play around with some online bit to int conversions, you will quickly get how it goes). Enemy vuln to fire and earth could have a value 0101 which is 5 (1+4). If you want to add air resistance (binary 10 === 2) you could add 2 to this value, so 5+2 = 7 and 7 represents vunlerability to fire, earth and air. The thing is, doing this is not safe, because it does not check if the flag already exists on it, adding air vuln to vlaue of 7 would make int to end with a value of 9 which is resistance to fire and earth.
But we have bitwise operators that are safer to do, in these examples I assume vulnerability to earth is 2:
1) adding earth vuln to list is: vulnerabilites = vulnerabilities | 2; (binary OR)
2) removing earth vuln from list: vulnerabilites = vulnerabilities & (~2); (binary AND NOT)
3) checeking if enemy is vulnerable: if ((vulnerabilities & 2) == 2) { damage*=2; }; (binary AND + equeals)
So whats about the "
Great video!
bro i have some advice. i think you should cut down to 1 video per week instead of 2, more time for your game and avoiding youtube burnout and more focus one video
hmmm, I went with a different approach, I used template* for enemies spawn point and utilities(inner walls and items), so there is like 12 template of spawning (at least for now), and it just enemy position, and when we add it with the template of inner walls, and item box, which is 20 variation, I got 240 variation of rooms. I got the inspiration from a android game called soul knight.
I personally would use physics layer and the collision matrix to only recognize collisions with the player instead of doing the comparison in code.
I understand your series name and it's clickable, but I do object to Real= Commercial.
Can you do a short tutorial on Vampire Survivors like game
Hey do you mind sharing a way on how to implement Steam Achievements into the game? i don't see many tutorials about this out there.
Regarding the bitwise operation: a byte is 8 bits, a word is 2 bytes, a long-word is 4 bytes, etc.
Lets just use one byte as an example
The decimal value 1 would be 00000001 in binary. If you shift that left 1 bit you get 00000010 which is 2 decimal. If you shift it left again you get 00000100 which is 4 decimal. etc.
Make sense?
You're not explaining why it's shifting or what shifting is really doing. You're just saying "if I push it, it moves", you're not explaining why it moves or how to get to move more or less space.
Think of it as a mathematical operation like addition or multiplication. This is no different then saying "if I multiply a number by 10 it 'moves' one place to the left (e.g. from the 10s place to the 100s place)." In the same way, if you shift a value one bit to the left, the result is that the value is moved one place to the left. The only difference is that it's base 2 not base 10.
Very small typo in the second chapter.. it says "Playe" instead of "Player"
I have to ask. Wouldn't using an object pooler to spawn in enemies be more efficient?
Depends on your needs. I would say a pooler is better for spawning in many of the same type of object, but not really for spawning in different kinds. A pooler normally wants to know how many will spawn at a time and which types and isn't very good with randomization. At least, not in my experience.
@@Dyanosis I do not disagree. On the other hand, I have made and seen code that allows for you to incorporate all types of many objects in an object pooler. It is far more difficult, but it is still possible. Heck, in the weirdest of situations, I have even pooled a pooler because of random data leak that could not be fixed with any other approach.
🎉🎉🎉🎉You can congratulate me, I'm the first! And so I understand what to sneeze at 🎉🎉🎉
FIRST
First