How to have only one tile selected at a time in a hex grid in Unity - c#

as the title says! I'm working on a sort of Civilization type city builder game as practice for the coming school year (only a second year video game programming student!).
I've already gotten a grid generated in game, which looks like:
This!
As you can see I've already gotten a rudimentary selection system set up, wherein currently I can only select one tile at a time until I deselect it, then I can select a new tile. The tiles are selected using an OnClick function tied to a collider on the prefab. (will include my code I have currently at the end!)
What I'm wondering how to do is have a tile deselect automatically whenever I select a new tile, so I have only one tile selected at a time.
This is what I have for now for selection.
public void OnMouseDown() {
if (GameManager.Instance.tileSelected == false) {
if (enabled == false) {
tileOutlineSprite.SetActive (true);
enabled = true;
GameManager.Instance.tileSelected = true;
this.tileInfo.text = tileType;
}
}
else if (enabled == true) {
tileOutlineSprite.SetActive (false);
enabled = false;
GameManager.Instance.tileSelected = false;
this.tileInfo.text = " ";
}
}
And this is what I'm currently using to generate my grid! I know it might be a lil messy for now, I'm planning on cleaning it up and refining it as I go on!
void generateMap() {
map = new List<List<TileSelect>>(); //generatign the playing field, making a grid of tile prefabs, and storing their positiosn in a 2d list
for (int i = 0; i < mapSizeX; i++) {
List <TileSelect> row = new List<TileSelect>();
for (int j = 0; j < mapSizeY; j++) {
if (i == 0) {
iDiff = 0.8f;
}
if (j % 2 == 0) {
iDiff = i + (.2f * (i+1));
} else if (i != 0) {
iDiff = i + 0.6f + (.2f * (i+1));
}
jDiff = j + (.04f * j);
int rand = Random.Range (1, 101);
if (rand <= 45) {
TileSelect tile = ((GameObject)Instantiate (HeavyForestTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Heavy Forest";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 45 && rand <= 70) {
TileSelect tile = ((GameObject)Instantiate (LightForestTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Light Forest";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 70 && rand <= 90 ) {
TileSelect tile = ((GameObject)Instantiate (GrassTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Grassland";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 90 && rand <= 97) {
TileSelect tile = ((GameObject)Instantiate (GrassRockTile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Light Rocks";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
} else if (rand >= 97 && rand <= 100) {
TileSelect tile = ((GameObject)Instantiate (GrassRock2Tile, new Vector3 (iDiff, jDiff, 0), Quaternion.Euler (new Vector3 ()))).GetComponent<TileSelect> ();
tile.gridPosition = new Vector2 (i, j);
tile.tileType = "Heavy Rocks";
tile.GetComponent<TileSelect> ().tileInfo = GameObject.Find ("InfoText").GetComponent<Text>();
row.Add (tile);
}
}
map.Add(row);
}
Oh! and the game is in 2d if that matters for a solution! Let me know if you need anymore info, I'll happily supply it!

A simple way would be to add an additional member to your game manager class:
public class GameManager
{
TileSelect _selectedTile;
public TileSelect selectedTile
{
get { return _selectedTile; }
set
{
//unhighlight the previous selected tile
_selectedTile = value;
//highlight the newly selected tile
}
}
...
}
Include a setter such that everytime you change the selected tile, it unhighlights the selected tile and highlights the new selected tile.
Simply change the selected tile when it is clicked:
void onClick(...)
{
...
//on raycast hit with the 2d tile (targetTile)
gameManager.selectedTile = targetTile;
}

Related

Collision twitching when player is on more than 1 block

I'm coding collision in my 2D tile-based game, coded with Monogame. I've ran into a problem where if my player is standing on 2 tiles at once, it starts to twitch because the collision resolving is running 2 times, I want this to only run once. How would I do this? Here is my code:
public void HandleCollisions()
{
Rectangle plyRect = ply.GetBounds(); //get player rectangle
Vector2 currentPos = new Vector2(plyRect.X, plyRect.Y); //get player current position
Vector2 xy = new Vector2( (float)Math.Floor((ply.pos.X + ply.tex.Width) / world.tileSize),
(float)Math.Floor((ply.pos.Y + ply.tex.Height) / world.tileSize)); //get tiles position based on player position
for (int x = (int)xy.X - 4; x <= (int)xy.X + 4; x++) //run through tiles near the player
{
for (int y = (int)xy.Y - 4; y <= (int)xy.Y + 4; y++)
{
if (x >= 0 && y >= 0 && x < world.GetWorldSize().X && y < world.GetWorldSize().Y) //check if tiles are within map
{
if (world.tiles[x, y] != null)
{
if (world.tiles[x, y].collision == Tile.Collision.SOLID) //check if tile is solid
{
Rectangle tileRect = world.tiles[x, y].GetRect(); //get the tiles rectangle
if (plyRect.Intersects(tileRect)) //check if intersecting
{
Vector2 depth = RectangleExtension.GetIntersectionDepth(plyRect, tileRect); //get intersecting depth
if (depth != Vector2.Zero)
{
float absDepthX = Math.Abs(depth.X);
float absDepthY = Math.Abs(depth.Y);
if (absDepthY < absDepthX)
{
currentPos = new Vector2(currentPos.X, currentPos.Y + depth.Y); //resolve Y collision first
}
else
{
currentPos = new Vector2(currentPos.X + depth.X, currentPos.Y); //then resolve X collision
}
}
}
}
}
}
}
}
ply.pos = currentPos; //set player position after the checking is done
}
Once one collision is detected break out of the for loop using a break; statement

How to create Unity 2D enemy AI varying directional Animation?

So basically what i am trying to do is create an animation script that does not just take simple direction like Up(0,1),Down(0,-1),Left(-1,0),Right(1,0)(Those numbers being vectors) but to have range of directions for to fill in the gap between those directions and play the more dominant direction Up,Down,Left,Right.
So each quarter section below to be set to an animation.
I have created a few scripts to try and achieve this i came up with one that wasn't perfect but it did work when dragging the enemies around in the scene editor while the game was running. When using their movements scripts for their movement it did not work. The animation script is below and underneath that how i move the enemies.
Below would be how i calculate the direction the enemy was moving:
IEnumerator directionFinder()
{
// while (true)
//{
Vector3 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
currentPosition = transform.position;
currentPosition.x = currentPosition.x - previousPosition.x;
currentPosition.y = currentPosition.y - previousPosition.y;
currentPosition.z = 0f;
angle = (Mathf.Atan2(currentPosition.y, currentPosition.x) * Mathf.Rad2Deg);
if(angle < 0)
{
angle = Mathf.Abs(angle) + 180;
}
// }
}
Then this is what calculated which animation to use:
void animationDirection()
{
if (angle == 0)
{
anim.SetInteger("Move", currentAnim);
}
if (angle > 45 && angle <135)//W
{
anim.SetInteger("Move", 1);
currentAnim = 1;
}
else if (angle > 135 && angle < 225)//A
{
anim.SetInteger("Move", 2);
currentAnim = 2;
}
else if (angle > 225 && angle < 315)//S
{
anim.SetInteger("Move", 3);
currentAnim = 3;
}
else if((angle < 45 && angle >= 0) || (angle > 315 && angle <= 0))//D
{
anim.SetInteger("Move", 4);
currentAnim = 4;
}
}
This using the code below it would move to the waypoint that was selected via the rest of my code that would just loop through a set of waypoints changing the waypoint when it arrived at one.
transform.position = Vector2.MoveTowards(transform.position, waypoints[waypointNUmber].transform.position, speed * Time.deltaTime);
Below I tried another way but it did not work and created weird buggy animations:
IEnumerator directionalAnimation()
{
while (true)
{
Debug.Log("working");
Vector2 previousPosition = transform.position;
yield return new WaitForSeconds(1/10);
Vector2 currentPosition = transform.position;
Vector2 direction = (previousPosition - currentPosition).normalized;
float moveX = direction.x;
float moveY = direction.y;
if (moveX < 0)
{
moveXNegative = true;
moveX = Mathf.Abs(moveX);
}
if (moveY < 0)
{
moveYNegative = true;
moveY = Mathf.Abs(moveY);
}
if (direction.magnitude != 0)
{
if (moveX > moveY)
{
if (moveXNegative)
{
//left
Walking = true;
anim.SetInteger("Move", 2);
moveYNegative = false;
}
else
{
//Right
Walking = true;
anim.SetInteger("Move", 4);
moveYNegative = false;
}
}
else if (moveY > moveX)
{
if (moveYNegative)
{
//Down
Walking = true;
anim.SetInteger("Move", 3);
}
else
{
//Up
Walking = true;
anim.SetInteger("Move", 1);
}
}
}
else
{
source.Pause();
Walking = false;
}
if (Walking)
{
source.UnPause();
}
}
}

Instantiate GameObjects over network

I have a piece of code that is supposed to generate a random tile map which it does for the server only. I can't figure out how to get it to sync to the players when they join it.How would I sync the prefabs that are instigated to the joining players? They load on one player (the server) but when another player joins they don't show up.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class GenerateLevel : NetworkBehaviour {
public int MapSize = 20;
public GameObject prefab;
public GameObject _LeftW;
public GameObject _RightW;
public GameObject _TopW;
public GameObject _Botw;
public GameObject SpawnLocRed;
public GameObject SpawnLocBlue;
public string map;
public override void OnStartServer()
{
//base.OnStartServer();
GetComponent<Renderer>().material.color = Color.blue;
}
void Start()
{
if (!isServer)
{
return;
}
for (int c = MapSize / 2; c > -MapSize / 2; c--)
{
for (int r = -MapSize / 2; r < MapSize / 2; r++)
{
var t = Instantiate(prefab, new Vector3(r, 0, c),
Quaternion.identity);
NetworkServer.Spawn(t);
Debug.Log("Col:" + c + " Row:" + r);
if (Random.Range(0, 3) == 2)
{
t.GetComponent<RAISE>().Run();
map += 1;
}
else
{
map += 0;
if (c < 0 - MapSize / 4 && r > 0 + MapSize / 4)
{
var red= Instantiate(SpawnLocRed, new Vector3(r, 2, c),
Quaternion.identity);
}
if (c > 0 + MapSize / 4 && r < 0 - MapSize / 4)
{
var blue= Instantiate(SpawnLocBlue, new Vector3(r, 2, c),
Quaternion.identity);
}
}
map += "r";
}
map += "c";
}
Debug.Log(map);
if (MapSize % 2 == 0)
{
GameObject wt = Instantiate(_TopW, new Vector3(-.5f, 0, -MapSize / 2), Quaternion.identity);
wt.transform.localScale = new Vector3(MapSize, 5, 1);
GameObject wb = Instantiate(_Botw, new Vector3(-.5f, 0, MapSize / 2+1), Quaternion.identity);
wb.transform.localScale = new Vector3(MapSize, 5, 1);
GameObject wl = Instantiate(_LeftW, new Vector3(-MapSize / 2-1, 0, .5f), Quaternion.identity);
wl.transform.localScale = new Vector3(1, 5, MapSize+2);
GameObject wr = Instantiate(_RightW, new Vector3(MapSize / 2, 0, .5f), Quaternion.identity);
wr.transform.localScale = new Vector3(1, 5, MapSize+2);
}
else
{
GameObject wt = Instantiate(_TopW, new Vector3(-.5f, 0, -MapSize / 2), Quaternion.identity);
wt.transform.localScale = new Vector3(MapSize-1, 5, 1);
GameObject wb = Instantiate(_Botw, new Vector3(-.5f, 0, MapSize / 2 + 1), Quaternion.identity);
wb.transform.localScale = new Vector3(MapSize-1, 5, 1);
GameObject wl = Instantiate(_LeftW, new Vector3(-MapSize / 2 - 1, 0, .5f), Quaternion.identity);
wl.transform.localScale = new Vector3(1, 5, MapSize + 1);
GameObject wr = Instantiate(_RightW, new Vector3(MapSize / 2, 0, .5f), Quaternion.identity);
wr.transform.localScale = new Vector3(1, 5, MapSize + 1);
}
Debug.Log(MapSize % 2);
}
Here is the updated snippet code
public GameObject[] tiles = new GameObject[1000];
public string map;
public override void OnStartServer()
{
//base.OnStartServer();
GetComponent<Renderer>().material.color = Color.blue;
}
public void OnPlayerConnected(NetworkPlayer player)
{
foreach(GameObject go in tiles)
{
NetworkServer.Spawn(go);
}
}
void Start()
{
if (!isServer)
{
return;
}
int temp = 0;
for (int c = MapSize / 2; c > -MapSize / 2; c--)
{
for (int r = -MapSize / 2; r < MapSize / 2; r++)
{
var t = Instantiate(prefab, new Vector3(r, 0, c), Quaternion.identity);
tiles[temp] = t;
NetworkServer.Spawn(t);
}
}
}
After some fiddiling I got the tiles to spawn but they are not syncing ot the characters the henerate tile is running for each client seperatrly.
You can't just use the Instantiate function to make prefabs show on the network. There are other things you must do:
1.You must register the prefab through the NetworkManager component. If there are more than one prefab, you must do this for all of them:
2.Attach NetworkIdentity component to the prefab. Note that if you also want to sync the objects transform, you must attach NetworkTransform to the prefab too.
3.After instantiating the prefab, use NetworkServer.Spawn to also instantiate it on the network too.
For example:
//Instantiate on this device
GameObject red = Instantiate(SpawnLocRed, new Vector3(r, 2, c),
Quaternion.identity);
//Instantiate it on all clients
NetworkServer.Spawn(red);
Finally, you may want to wrap all the code around inside isLocalPlayer to make sure you only instantiate from the local player.
if (isLocalPlayer)
{
//Instantiate on this device
GameObject red = Instantiate(SpawnLocRed, new Vector3(r, 2, c),
Quaternion.identity);
//Instantiate it on all clients
NetworkServer.Spawn(red);
}

Cannot make a Rectangle to display with Unity + Oculus Rift

I'm trying to display a simple rectangle right in front of my OVRPlayerController's camera but it seems to be impossible.
I think it might have something to do with the fact that Rect is 2D and my environment is 3D. Does that make sense?
The code is the following (I have ommited the unnecessary stuff):
static int MAX_MENU_OPTIONS = 3;
public GameObject Menu;
private bool showMenu = false;
private float menuIndex = 0;
private bool hasPressedDirectionalPad = false;
public Transform[] buttons = new Transform[MAX_MENU_OPTIONS];
private static Texture2D staticRectTexture;
private static GUIStyle staticRectStyle;
bool DpadIsPressed() {
if (!hasPressedDirectionalPad && Input.GetAxis("DpadY") != 0 && hasPressedDirectionalPad == false){
menuIndex += Mathf.Sign(Input.GetAxis("DpadY")) * (-1);
if (menuIndex < 0) menuIndex = 0;
else if (menuIndex > MAX_MENU_OPTIONS-1) menuIndex = MAX_MENU_OPTIONS-1;
hasPressedDirectionalPad = true;
}
if(Input.GetAxis("DpadY") == 0){
hasPressedDirectionalPad = false;
}
return hasPressedDirectionalPad;
}
void Start() {
Menu.SetActive(false);
staticRectTexture = new Texture2D(1, 1, TextureFormat.RGB24, true);
staticRectStyle = new GUIStyle();
}
void Update() {
if (Input.GetButtonDown("A")) {
DoAction ();
print ("A key was pressed");
}
if (Input.GetButtonDown("Options")) {
showMenu = !showMenu;
if (showMenu) {
Time.timeScale = 0;
menuIndex = 0;
Menu.transform.rotation = this.transform.rotation;
Menu.transform.position = this.transform.position;
} else
Time.timeScale = 1;
}
if (DpadIsPressed ()) {
print ("Dpad key was pressed and menuIndex = " + menuIndex);
}
if (showMenu) {
Menu.SetActive (true);
}
if (!showMenu) {
Menu.SetActive (false);
}
}
void OnGUI() {
if (showMenu) {
Vector3 offset = new Vector3(0, 0, 0.2f);
Vector3 posSelectRectangle = buttons[(int)menuIndex].transform.position + offset;
Rect selectionRectangle = new Rect(posSelectRectangle.x - (float)177/2,
posSelectRectangle.y - (float)43/2,
177.0f, 43.0f);
GUIDrawRect(selectionRectangle, new Color(255.0f, 0, 0));
}
}
void DoAction () {
if (menuIndex == 0)
Salir ();
/*else if (menuIndex == 1)
Guardar ();*/
else if (menuIndex == 2)
Salir ();
}
public static void GUIDrawRect(Rect position, Color color ) {
staticRectTexture.SetPixel( 0, 0, color );
staticRectTexture.Apply();
staticRectStyle.normal.background = staticRectTexture;
GUI.Box( position, GUIContent.none, staticRectStyle );
}
The functions are visited, but the rectangle doesn't show up. Do you see the mistake? Maybe it has something to do with the Oculus Rift?
OnGUI and Screen-space Canvas are not supported in VR mode. This is because there is no way to handle stereoscopic rendering. (Note: They will render to the duplicate display on the user's PC though).
If you want to render in front of the user's camera (like a HUD), you can:
Use a Canvas:
Create a canvas, then add your UI, and set the canvas in world-space. Parent the canvas to the VR Camera game object, and scale it down (it defaults to very very big) and rotate it so it faces the camera.
Or, Use 3D:
Create a 3d Object (Plane, Cube, Quad, whatever!) and parent it to your VR Camera. You can use standard 3d techniques to update it's texture or render texture.

C# XNA - Ball & Brick Continuous Collision Detection

I've been having some trouble applying correct velocity changes to my ball when it hits bricks in my Breakout clone. In a previous question, I was advised to use continuous collision detection, as well as other methods such as finding the intersection between the ball and the brick when it hits a corner to determine which direction the ball should reflect. I've applied this to my code below, but there are still occasions when the ball will just completely plow through a collection of bricks. This is more noticeable when it hits moving bricks.
In Level.cs Update method:
Bricks.ForEach(brick => Balls.ForEach(ball => ball.Collide(brick)));
In Ball.cs:
public bool Touching(Brick brick)
{
var position = Position + (Velocity * Speed);
return position.Y + Size.Y >= brick.Position.Y &&
position.Y <= brick.Position.Y + brick.Size.Y &&
position.X + Size.X >= brick.Position.X &&
position.X <= brick.Position.X + brick.Size.X && brick.Health > 0 && brick.Lifespan == 1F;
}
public void Collide(Brick brick)
{
if (!Touching(brick)) return;
var position = Position + (Velocity * Speed);
var bounds = new Rectangle((int)position.X, (int)position.Y, Texture.Width, Texture.Height);
var nonCCDBounds = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width, Texture.Height);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var change = new Vector2(Velocity.X, -Velocity.Y);
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width < intersection.Height || nonCCDIntersection.Width < nonCCDIntersection.Height){
change = new Vector2(-Velocity.X, Velocity.Y);
}
}
if (bounds.Intersects(brick.Top))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y - 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y > brick.Position.Y)
change = new Vector2(-Velocity.X, Velocity.Y);
}
if (bounds.Intersects(brick.Bottom))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X, brick.GridPosition.Y + 1)) != null)
change = new Vector2(-Velocity.X, Velocity.Y);
else if ((Position - Velocity).Y < brick.Position.Y + brick.Texture.Bounds.Height)
change = new Vector2(-Velocity.X, Velocity.Y);
}
ReflectBall(brick, change);
return;
}
if (bounds.Intersects(brick.Left) || bounds.Intersects(brick.Right))
{
var change = new Vector2(-Velocity.X, Velocity.Y);
if (bounds.Intersects(brick.Top) || bounds.Intersects(brick.Bottom))
{
var intersection = Rectangle.Intersect(bounds, brick.Texture.Bounds);
var nonCCDIntersection = Rectangle.Intersect(nonCCDBounds, brick.Texture.Bounds);
if (intersection.Width > intersection.Height || nonCCDIntersection.Width > nonCCDIntersection.Height)
{
change = new Vector2(Velocity.X, -Velocity.Y);
}
}
if (bounds.Intersects(brick.Left))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X - 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X > brick.Position.X)
change = new Vector2(Velocity.X, -Velocity.Y);
}
if (bounds.Intersects(brick.Right))
{
if (level.GetBrick(new Vector2(brick.GridPosition.X + 1, brick.GridPosition.Y)) != null)
change = new Vector2(Velocity.X, -Velocity.Y);
else if ((Position - Velocity).X < brick.Position.X + brick.Texture.Bounds.Width)
change = new Vector2(Velocity.X, -Velocity.Y);
}
ReflectBall(brick, change);
}
}
public void ReflectBall(Brick brick, Vector2 reflection)
{
Position = Position - Velocity;
Velocity = reflection;
if (brick.Health < 9)
{
brick.Health--;
brick.Life --;
}
if (brick.Health > 0 && brick.Life > 0)
{
brick.Texture = Assets.GetBrick(brick.TextureName, brick.Health);
}
}
It's a bit of a mess but it's the closest I've got to having decent collision. It would be much easier if there was a fast way of finding out collision points and applying correct velocity changes.

Categories

Resources