I am trying to program a Unity Minesweeper game, and I am almost done, but as part of it I have a boolean variable called "isCovered" that works out if the square has been clicked or not.
This variable is used to see if the player has won yet. I have a 2d array that stores all of my squares, and to see if you win yet, it checks the array for covered, non mine squares, and if it finds them, concludes that you have not won yet.
However, when I test it, the code does not seem to find any squares that are covered, even though I can see that there are some. I concluded this because it outputs the "you win" message every time I click a safe square, even when I am not finished.
My variable is declared using this code:
public class Square : MonoBehaviour {
//creates boolean variable
public bool IsCovered() {
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
}
and the code to determine if you win, which is within a different class (that is my array/grid) is:
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
The asset that is used for the default texture is called "Uncleared proposed", rather than "Default" so I don't know if that is an issue.
My entire "Square" code for each individual tile is:
using System.Collections;
using UnityEngine;
public class Square : MonoBehaviour
{
//is this dangerous?
public bool dangerous;
//creates boolean variable
public bool IsCovered()
{
//checks if the square still uses the default texture.
return GetComponent<SpriteRenderer>().sprite.texture.name == "Default";
}
//different textures
public Sprite[] emptyTextures;
public Sprite dangerTexture;
// Use this for initialization
void Start()
{
//game randomly decides if square is dangerous
//10% chance for initial testing
//Chance likely to be changed at some point
dangerous = Random.value < 0.1;
//registers square in grid
int x = (int)transform.position.x;
int y = (int)transform.position.y;
Grid.elements[x, y] = this;
}
void OnMouseUpAsButton()
{
// It's dangerous
if (dangerous)
{
//uncover all mines
//calls for procedure "uncoverDanger()" from class "Grid"
Grid.uncoverDanger();
//ToDo: display loss screen
// game over
print("Game Over");
}
// It's not a mine
else
{
// change to correct texture
int x = (int)transform.position.x;
int y = (int)transform.position.y;
loadTexture(Grid.adjacentCount(x, y));
//Flood Fill nearby area
Grid.FFuncover(x, y, new bool[Grid.w, Grid.h]);
// checks if the player has won
if (Grid.gameWon() == true)
//tells the player they have won
print("you win");
}
}
//Loads a different texture
public void loadTexture(int adjacentCount)
{
//checks to see if square is dangerous
if (dangerous)
//Changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = dangerTexture;
//if not dangerous
else
//changes to correct texture using Unity Component
GetComponent<SpriteRenderer>().sprite = emptyTextures[adjacentCount];
}
}
and my Grid class code is:
using System.Collections;
using UnityEngine;
public class Grid {
public static int w = 10; // w is the width of the grid (10)
public static int h = 13; // h is the height of the grid (13)
public static Square[,] elements = new Square[w, h];
//Uncover all mines when losing
public static void uncoverDanger() {
foreach (Square elem in elements)
if (elem.dangerous)
elem.loadTexture(0);
}
// Determines if the square is dangerous
public static bool dangerAt(int x, int y)
{
// Makes sure the square is in the range
if (x >= 0 && y >= 0 && x < w && y < h)
// Checks if the square is dangerous
return elements[x, y].dangerous;
return false;
}
// Counts number of dangerous squares adjacent
public static int adjacentCount(int x, int y)
{
int count = 0;
//checks adjacent squares
//potentially adds to counter
if (dangerAt(x, y + 1)) ++count; // top
if (dangerAt(x + 1, y + 1)) ++count; // top-right
if (dangerAt(x + 1, y)) ++count; // right
if (dangerAt(x + 1, y - 1)) ++count; // bottom-right
if (dangerAt(x, y - 1)) ++count; // bottom
if (dangerAt(x - 1, y - 1)) ++count; // bottom-left
if (dangerAt(x - 1, y)) ++count; // left
if (dangerAt(x - 1, y + 1)) ++count; // top-left
//outputs value
return count;
}
// Flood Fill (FF) safe squares
public static void FFuncover(int x, int y, bool[,] visited)
{
// Checks if coordinates in range
if (x >= 0 && y >= 0 && x < w && y < h)
{
// Filled already?
if (visited[x, y])
return;
//uncovers a square
elements[x, y].loadTexture(adjacentCount(x, y));
//checks if adjacencyCount is not 0
if (adjacentCount(x, y) > 0)
//ends algorithm
return;
// set square as visited
visited[x, y] = true;
// repeates with all 4 adjacent squares (not using diagonal)
FFuncover(x - 1, y, visited);
FFuncover(x + 1, y, visited);
FFuncover(x, y - 1, visited);
FFuncover(x, y + 1, visited);
}
}
public static bool gameWon()
{
// Checks all squares in the array
foreach (Square elem in elements)
// Tries to find a covered, safe square
if (elem.IsCovered() && !elem.dangerous)
// The player is not finished
return false;
// The player has won
return true;
}
}
I am a real newbie to unity so it might be something stupid, any help would be greatly appreciated.
Turns out I am an idiot, I just had to change the name of the texture in the variable code to "Uncleared proposed", I don't know why it didn't work earlier.
Related
I have a code for a crafting system that checks if the inventory has the ingredients needed to craft an item and adds a button to craft it. The problem is when I want to position my button it goes way off the canvas. I have seen some people saying that it has something to do with rect transform. I've been stuck with it for over an hour. Any help is appreciated.
I have tried
removing the setparent() function,
using anchoredPosition,
using localPosition
My code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crafting : MonoBehaviour
{
public List<recipe> recipes = new List<recipe>();
public GameObject base_item, parent;
List<GameObject> items = new List<GameObject>();
public int y = 75;
public int x = -45;
public Inv inv;
private void Start()
{
inv = GetComponent<Inv>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
checkitems();
Debug.Log("y = " + y + " x = " + (x - 40));
}
}
public void checkitems()
{
for (int i = 0; i < recipes.Count; i++)
{
recipe r = recipes[i];
for (int x = 0; x < r.ingredients.Count; x++)
{
if (!inv.hasitem(r.ingredients[x])){
return;
}
}
showitem(r.result);
}
}
public void onClick(int _slot)
{
recipe r = recipes[_slot];
for (int i = 0; i < r.ingredients.Count; i++)
{
inv.removeitem(inv.getitem(r.ingredients[i]));
}
inv.additem(inv.getFirstAvailable(), r.result, r.stack);
}
public void showitem(string name)
{
GameObject obj = Instantiate(base_item);
if (items.Count != 0)
{
if (((items.Count) % 3) != 0)
{
Debug.Log("first thing");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
else if (((items.Count + 1) % 3) == 0)
{
Debug.Log("second thing");
x = -45;
Debug.Log("x + 40");
y = y + 40;
Debug.Log(" y + 40");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
}
}else
{
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
}
}
Blue circle where it spawns. Red circle where I want it to be
Seems you are confusing a bunch of terms for being the issue of your problem. Firstly I want to address the red X over your scroll bar. Whenever this occurs, it means that your RectTransform of this UI object has been dragged from its positive vertices to negative or vice versa, causing it to almost invert. I would correct this but it is not the reason your objects are not childing correctly.
Generally, with UI objects, I would never use LocalPosition, just AnchoredPosition. LocalPosition is a field from Transform which I believe RectTransform inherits from. As RectTransforms have a lot of modifications to their position from pivots, anchors, and anchored positions, the LocalPosition will most likely need to recalculate data to properly move the object, whereas AnchoredPosition has already done these calculations.
I believe the issue with your current code is how you are using SetParent. There is a second parameter of SetParent which governs whether the object keeps the same position based in world space after being childed. As you are not passing in a new bool for this parameter, it is defaulting to true. As you want your objects to be childed to the parent but not keep their world space positions, you would want to pass in false.
In your case, as it looks as if you want to set objects in a grid-like pattern childed to this ScrollRect, I would attach a GridLayoutGroup to the Content of your scroll and child the new objects to this object. You can set the max columns of this grid and spacing to give the same layout you are attempting to achieve in code.
To summarize, I would remove all the hand placement you are doing in code with LocalPosition and AnchorPosition and just attach a GridLayoutGroup. To fix the current positioning of your objects relative to the parent, change all lines of obj.transform.SetParent(parent.transform); to obj.transform.SetParent(parent.transform, false);. If you want to keep changing position locally in code instead of a layout element, use SetParent first, and use AnchoredPosition instead of LocalPosition as the SetParent with false passed in will override the position you set.
I have a Dictionary of Circles (random distributed in 2D-space) and a Position in 2D-space.
The method should return just ANY circle, where the Position is within the radius of the circle. (There might be even more than one, but I don't care about which one it chooses)
My current implementation looks like this:
int GetAnyCircleWithinRadius(Dictionary<int, Circle>circles, Position position)
{
int circleIndex = -1;
Parallel.ForEach(circles.Values, (circle, state) =>
{
double deltaX = cirlce.center.x - position.x;
double deltaY = cirlce.center.x - position.x;
double distance = Math.Abs(deltaX * deltaX + deltaY * deltaY);
if (distance < circle.radius)
{
state.Break();
circleIndex = circle.index;
}
}
return circleIndex;
}
Basically it runs through all circles in parallel on the CPU and checks if the distance to the center is smaller than its radius, which would mean that the position is inside the circle.
Now my question is:
Is there a simple way, to run the same routine on the GPU instead of the CPU?
What I tried so far is a bit playing arround with Cloo and I was able to run a prime-searcher (see below) but I have no idea of how I can translate my own "circle-problem"-C# code in this program.
using Cloo.Extensions;
...
static void Main(string[] args)
{
primes.ClooForEach(IsPrime);
}
static string IsPrime
{
get
{
return
#"
kernel void GetIfPrime(global int* message)
{
int index = get_global_id(0);
int upperl = (int)sqrt((float)message[index]);
for(int i = 2; i <= upperl; i++)
{
if(message[index]%i==0)
{
message[index]=0;
return;
}
}
}";
}
}
I'm also glad to receive any other performance hints! :)
I created my tile map and my player with movement.
I'm now trying to create the collision and I feel i'm on the right track.
Here is how I've created the map.
List<Texture2D> tileTextures = new List<Texture2D>();
int tileWidth = 60;
int tileHeight = 60;
public int[,] Map = new int[,]
{
{2,2,2,2,2,2,2,2,2,2},
{2,2,2,2,1,2,2,2,2,2},
{2,2,2,2,2,2,2,2,2,2},
{2,2,2,2,2,2,2,2,2,2},
};
public void Draw(SpriteBatch spriteBatch)
{
int tileMapWidth = Map.GetLength(1);
int tileMapHeight = Map.GetLength(0);
for (int x = 0; x < tileMapWidth; x++)
{
for (int y = 0; y < tileMapHeight; y++)
{
int textureIndex = Map[y, x];
Texture2D texture = tileTextures[textureIndex];
spriteBatch.Draw(
texture,
source = new Rectangle(x *myTile.Width,
y * myTile.Height,
tileWidth,
tileHeight),
Color.White);
}
}
}
I am checking the 2d array coords with this condition and checking to see if a specific tile is there, where I can then set my previous location if it is true.
I'm currently testing on 1 tile atm.
public void Update(GameTime gameTime)
{
prevPosition = position;
input(gameTime);
if(tiles.Map[(int)playerPosition.X/60,(int)playerPosition.Y/60] == 1)
{
position = prevPosition;
}
}
However my player position keeps going out of the index bounds of the 2D array and I believe I need to scale it down so that it stops this, I've tried dividing the play coords by the width of the tiles but that hasn't worked.
If anyone can help me with the correct scaling I would be very appreciative.
This will happen if your player's position is like -x,y or x,-y or maybe -x,-y. Yor approach might be better if you'd make a fuction like this one
public bool CollidesWithWall(int x, int y)
{
if(x < 0 || x > *matrix width* - 1) return false;
if(y < 0 || y > *matrix height* -1) return false;
if (Map[x,y] == 1) return true;
return false;
}
and use it insead of the line tiles.Map[(int)playerPosition.X/60,(int)playerPosition.Y/60]
Or, if you need the type of tile returned
public int CollidesWithWall(int x, int y)
{
if(x < 0 || x > *matrix width* - 1) return -1;
if(y < 0 || y > *matrix height* -1) return -1;
return Map[x,y];
}
By doing it this way, you'll know if you stumbled upon a health potion (just set it's ID to like 3) or a wall (with ID of 1 or something, that's tottaly up to you) and if it is 0, it's empty space (or maybe -1). Notice that the "-1" part is totaly up to you. Just write down a list of id's that youll have and which items they present.
Other suggestions
Try if(tiles.Map[(int)(playerPosition.X/60f),(int)(playerPosition.Y/60f)] == 1)
I am currently writing an OpenGL application using the SharpGL library and I am trying to simply create a 3x3x3 set of cubes arranged in a symmetric grid.
I am currently seeing some strange behaviour exhibited in the following picture:
This has me completely stumped as I can see no reason why the code is missing out the last 3 blocks. The method in charge of creating the cube looks like this:
private void CreateCube2(OpenGL gl, int cubeSize)
{
gl.PushMatrix();
const float spacing = 2.5f;
for (int z = 0; z < cubeSize; z++)
{
for (int y = 0; y < cubeSize; y++)
{
for (int x = 0; x < cubeSize; x++)
{
var cube = new Cube();
ColourCube(cube, cubeSize, x, y, z);
cube.Render(gl, RenderMode.Render);
gl.Translate(spacing, 0, 0);
}
gl.Translate(-spacing * cubeSize, spacing, 0);
}
gl.Translate(0, -spacing * cubeSize, spacing);
}
gl.PopMatrix();
}
where the definition of ColourCube is as follows:
private bool m_blackCubeMiddle = true;
private void ColourCube(Cube cube, int size, int x, int y, int z)
{
cube.Faces[0].Material = (!m_blackCubeMiddle || y == 0) ? WhiteMaterial : BlackMaterial; // Bottom
cube.Faces[1].Material = (!m_blackCubeMiddle || y == size - 1) ? YellowMaterial : BlackMaterial; // Top
cube.Faces[2].Material = (!m_blackCubeMiddle || x == size - 1) ? GreenMaterial : BlackMaterial; // Right
cube.Faces[3].Material = (!m_blackCubeMiddle || x == 0) ? BlueMaterial : BlackMaterial; // Left
cube.Faces[4].Material = (!m_blackCubeMiddle || z == 0) ? OrangeMaterial : BlackMaterial; // Front
cube.Faces[5].Material = (!m_blackCubeMiddle || z == size - 1) ? RedMaterial : BlackMaterial; // Back
}
The entire project can be downloaded from here.
The strange behaviour is caused by a bug in SharpGL 'Polygon.Render' method. It is not caused by your own code. When you call Triangulate on the cube, it already shows 27 cubes on screen in the correct positions. But even then, SharpGL incorrectly renders one of the cubes.
Having looked at the source code for ShapGL's Cube & Polygon classes I'ld suggest to write your own cube class. The implementation of the Cube class, that you are using now, is absolutely horible from several standpoints (GL_POLYGON (deprecated), immediate mode vertex submission (deprecated))
I would like to say sorry in advance for the amount of code I will post, but I can't seem to get my collision detection to work, the player and the objects pass through each other with no effect when I play test.
I receive 0 warnings or errors, but the Player playerBot object does not seem to interact with any of the level items I have imported from GLEED2D.
One thing I did was to store the values of my item's rectangles and color arrays in lists so they could be all easily iterated through, maybe this is the source of the problem.
If you can spot why my code is not working I will be hugely grateful. I have removed any code that is definitely not relevant, and I am running VS 2010 with GLEED2D 1.3 if that helps.
Thanks again.
// Item Class ImageItem Downcast
public class ImageItem : Item
{
public Texture2D Texture;
}
// Level
Level level;
// Ints
int iNumOfItems = 0;
int iTextureDataListNum = 0;
int iRectangleListNum = 0;
// Lists
List<Color []> itemTextureDataList = new List<Color[]>();
List<Rectangle> itemRectangleList = new List<Rectangle>();
protected override void Initialize()
{
if (filename.Length > 0) level = Level.FromFile(filename, Content);
else level = Level.FromFile("level1.xml", Content);
foreach (Layer layer in level.Layers)
{
foreach (Item item in layer.Items)
{
iNumOfItems =+ 1;
}
}
// Creates Player Ship
playerBot = new Player(new Vector2(400f, 240f), new Vector2(0f, 0f));
base.Initialize();
}
protected override void LoadContent()
{
Texture2D pixel = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
pixel.SetData(new[] { Color.White });
spriteBatch = new SpriteBatch(GraphicsDevice);
// Player Bot
playerBot.LoadContent(Content, "Images/Player Bot Sprite Sheet", 40, 40, 4);
// Assigns level textures color data to array
foreach (Layer layer in level.Layers)
{
foreach (Item item in layer.Items)
{
ImageItem imageItem = item as ImageItem;
if (imageItem != null)
{
Texture2D texture = imageItem.Texture;
itemTextureDataList[iTextureDataListNum] = new Color[imageItem.Texture.Width * imageItem.Texture.Height];
imageItem.Texture.GetData(itemTextureDataList[iTextureDataListNum]);
iTextureDataListNum++;
}
}
}
// Creates a rectangle for every level texture
foreach (Layer layer in level.Layers)
{
foreach (Item item in layer.Items)
{
ImageItem imageItem = item as ImageItem;
if (imageItem != null)
{
itemRectangleList[iRectangleListNum] = new Rectangle((int)imageItem.Position.X, (int)imageItem.Position.Y, imageItem.Texture.Width, imageItem.Texture.Height);
iRectangleListNum++;
}
}
}
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void Update(GameTime gameTime)
{
// Player Update
playerBot.Update(gameTime);
((Sprite)playerBot).Update(gameTime);
// Check for player collisons with level
for (int i = 0; i < iNumOfItems - 1; i++)
{
if (IntersectPixels(playerBot.colRectangle, playerBot.textureDataArray, itemRectangleList[i], itemTextureDataList[i]) == true)
{
playerBot.StopMovement();
}
}
base.Update(gameTime);
}
// Level Collision Detection Method
static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
{
// Find the bounds of the rectangle intersection
int top = Math.Max(rectangleA.Top, rectangleB.Top);
int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
int left = Math.Max(rectangleA.Left, rectangleB.Left);
int right = Math.Min(rectangleA.Right, rectangleB.Right);
// Check every point within the intersection bounds
for (int y = top; y < bottom; y++)
{
for (int x = left; x < right; x++)
{
// Get the color of both pixels at this point
Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];
// If both pixels are not completely transparent
if (colorA.A != 0 && colorB.A != 0)
{
// Then an intersection has been found
return true;
}
}
}
// No intersection fond
return false;
}
// Sprite Class
public void Update(GameTime gameTime)
{
textureDataArray = new Color[texture.Width * texture.Height];
texture.GetData(textureDataArray);
// Player Class
public void StopMovement()
{
velocity.X *= -1;
velocity.Y *= -1;
}
The first thing I'll say here is, you should aim to be more Object Oriented in your approach. Storing a list of textures and a list of rectangles alongside your list of Items is "bad" technique and going to eventually cause you some massive headaches when it comes to debugging.
So first of all, instead of having a list of Color[] and a list of Rectangle, add one Color[] and one Rectangle to your ImageItem class and work with those instead, or at least create a little class called "CollisionData" that has a Rectangle and a Color[] and store those in a single list.
Secondly, note that there is a Rectangle.Intersect(Rectangle A, Rectangle B) that gets your the rectangle of intersection. So you can tidy up your code a bit by using that.
Your color checking can be simplified to (ColorA.A * ColorB.A != 0) as either being zero will
cause the result to be zero.
Regarding not getting any errors, put a breakpoint at the start of the collision checking loop. Does the application break? If yes, what is the value of iNumItems? (you can hover over it to see the current value at point of breaking). If no, then that section of code isn't being reached. Put another breakpoint a bit further back and a bit further back until it gets hit, then figure out why the code isn't executing.