First of all: I'm quite new to unity and c#, so please be nice if I ask dumb questions.
I'm trying to make a game like minesweeper. So I want an easy grid with covered tiles and if you klick on one, it opens up.
I use a main script I create a grid like so:
private void CreateTieleSet(int width, int height){
_tiles = new Dictionary<Vector2, Tile>();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
var spawnedTile = Instantiate(_tilePrefab, new Vector3(x, y), Quaternion.identity);
spawnedTile.name = $"Tile {x} {y}";
spawnedTile.status = field[x, y];
_tiles[new Vector2(x, y)] = spawnedTile;
}
}
}
I use the function
public Tile GetTileAtPosition(Vector2 pos)
{
if(_tiles.TryGetValue(pos, out var tile))
{
return tile;
}
return null;
}
to get the tile at the position xy, but I can't use it since
Tile tempTile = GetTileAtPosition(new Vector2(x, y));
tempTile.ChangeSprite(field[x, y]); //example-funktion from tile-script
allways results in NullReferenceExeption-Error. I know the probleme, since I allways struggle with using scripts from other tiles. Bus usually I can use [SerializeField] Tile... and than dragdrop it onto it. In this case however I obvioulsy can't do that.
Btw: I realy tried to solve this problem with other solutions found online, but everyone has complete different ideas how to do it and nothing seems to work for me :/
Your best bet for this one is going to be using Unity's built-in Tilemaps - you'll want to take your sprites for Minesweeper and add them to a TilePalette, and paint them onto a GameObject with a Grid component, with children GameObjects holding a Tilemap component - one per layer. Then, when you want to change the sprite, you can do something like the following:
using UnityEngine;
using UnityEngine.Tilemaps;
class GridManager
{
[SerializeField] Tilemap gridDisplayLayer;
public void ChangeSpriteAtLocation(Vector3Int cellLocation, Tile tileToChangeTo)
{
gridDisplayLayer.SetTile(cellPosition, tile);
}
}
If you want the simplest answer, just add Tile members to the class above for swapping between them and call ChangeSpriteAtLocation during runtime. Might need to change the flags on the Tilemap to change the sprite and such, but I think that should be good enough to get you started :) When you generate the TilePalette, Unity will give you Tile assets to save that you can then reference for the above example method.
I'm still getting all the intricacies of Tilemaps myself, though, and I'm definitely more design minded, so I won't be surprised if someone has a more succinct or efficient solution.
Hope this helps get you started, though!
Vellv
Related
I'm doing a unity project for an exam I'm attending, so I'm not really good at unity, hope you can help.
I'm doing this 2d endless runner game and I created an empty object to set inactive all the platforms that the player has overtaken. The object moves forward and when its position is > than the platform position, it sets it inactive (since destroying them also removes them from the list so it wouldnt work for others platforms of the same kind). My problem is that I've created a list in which I put all the types of my prefabs but the way I did it, it only sets inactive the first prefab of a kind and then stops working.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DistruttoreLivelli : MonoBehaviour
{
public List<GameObject> objs;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < objs.Count; i++)
{
if (objs[i].transform.position.x < transform.position.x)
{
objs[i].SetActive(false);
}
}
}
}
How can I manage to make it go forever? I tried to use a while but it makes unity crash. :/
Ty for the help!
The easiest way to make it endless is to move the objects to the furthest position instead of deactivating them. Instead of objs[i].SetActive(false); it should be something like:
Vector3 endOfTheLinePos = objs[i].transform.position;
endOfTheLinePos .x = maxX;
objs[i].transform.position = endOfTheLinePos;
With maxX being a float parameter of the highest value of x that you are using on the objects.
Also, as already pointed out, on the for you should use i < objs.Count instead.
I have created a game in which you can control X characters at the same time in the same form and they can die at any time. My problem is when I want the game camera to include all these gameobjects.
I thought that a good option is to calculate the central point between the gameobjects in the scene and make the camera follow that point at a certain distance.
I already have the camera code, but I still need to know how to get that central point or another way of doing it. In addition, the camera does not follow any of the axes (X, Y, Z) linearly, since it is placed in such a way that is the view is isometric (the game is in 3D).
As a last important fact, it is that all gameobjects that are running in the game (that are alive), are stored in a public static List <GameObject> to be able to access the components of these gameobjects at any time. Also, if a character (gameobject) dies or is born, the list is updated without problems.
I leave you a graphic example with three different cases, being the black points, the characters that are in the scene (gameobjects) and the red points, the central point (vector) that I would like to find.
Also, I leave the camera code so you can test if you have any solution:
public class Camera_Movement : MonoBehaviour {
Vector3 newPos;
public static List<GameObject> playersInGame = new List<GameObject>();
void Update() {
// Get Central Vector
// Replace playersInGame[0].transform.position with central vector
//newPos = Vector3.Lerp(gameObject.transform.position, "central vector", Time.deltaTime);
newPos = Vector3.Lerp(gameObject.transform.position, playersInGame[0].transform.position, Time.deltaTime);
gameObject.transform.position = new Vector3(newPos.x, newPos.y, newPos.z);
}
}
Thank you very much in advance!
You need to take the average x and and average y.
That would look like the following:
var totalX = 0f;
var totalY = 0f;
foreach(var player in playersInGame)
{
totalX += player.transform.position.x;
totalY += player.transform.position.y;
}
var centerX = totalX / playersInGame.Count;
var centerY = totalY / playersInGame.Count;
Let me know if this works for you (don't have access to Unity at the moment), but I put together an example here: https://dotnetfiddle.net/jGd99I
To have a solution that your camera can be best positioned to see all of your objects, then try this:
public Vector3 FindCenterOfTransforms(List<Transform> transforms)
{
var bound = new Bounds(transforms[0].position, Vector3.zero);
for(int i = 1; i < transforms.Count; i++)
{
bound.Encapsulate(transforms[i].position);
}
return bound.center;
}
I have a programmatically generated board of a certain size. In order to see it, I would normally have to hit run, which is quite annoying and makes it hard to visualize.
Is it possible to make, and thus be able to visualize, the board via InitializeOnLoad?
I gave it a try:
Setup.cs
[InitializeOnLoad]
public class Setup : MonoBehaviour {
public static Map initialMap;
static Setup(){
initialMap = new Map ();
initialMap.createMap ();
}
}
Map.cs
public class Map {
private Tile[,] tiles= new Tile[5,5];
//I had Resources.Load here, but apparently thats not allowed either...
public GameObject defaultObj= new GameObject("MyCreatedGO");
public Map (){
Debug.Log("In Constructor");
}
public void createMap(){
for (int x = 0; x < tiles.GetLength(0); x += 1) {
for (int y = 0; y < tiles.GetLength(1); y += 1) {
// Tile Instantiates the defaultObj
tiles [x, y] = new Tile (defaultObj, new Vector3 (x, y, 0));
}
}
}
}
But unity responded by complaining that
UnityException: Internal_CreateGameObject is not allowed to be called
from a MonoBehaviour constructor (or instance field initializer), call
it in Awake or Start instead. Called from MonoBehaviour 'Setup' on
game object 'Map'.
Is the engine even in a state where it can make objects when the InitializeOnLoad-ed static constructor happens?
If this isn't possible, how are you supposed to visualize a procedurally generated map in Unity?
I'm not sure if Unity can do that, but you can work around it.
Make a width and height variable, then expose them to the editor - either by making them public or by [SerializeField] attribute, and then edit them in the inspector while the editor is running the game. Use those variables for your x and y values in createMap(). Then you'd just have to add a section like this in Update(), to generate a new map on left click:
if (Input.GetMouseButtonDown(0))
{
// call generate map here
}
Check out this tutorial, too: https://unity3d.com/learn/tutorials/projects/procedural-cave-generation-tutorial/cellular-automata
You can create a custom editor for your Setup script.
You can add a couple of buttons to it like Create and Destroy and, onClick, you can execute every code you want at edit-time as opposed to runtime.
Here's the manual with useful informations on how to execute code in the editor and a video tutorial for writing Editor extensions.
I have this problem, that I want to be able to click on a "tile" on the screen and then a pop-up menu should be shown just next to the tile. I can click on the tile and then a pop-up menu shows up but not where I want it.
On the picture here I've clicked on the top left one.
My code for placing the picture is as following:
using UnityEngine;
using System.Collections;
public class TowerMenu : MonoBehaviour
{
bool showMenu = false;
float x;
float y;
GUIStyle myStyle;
public Texture2D[] towers;
void OnGUI()
{
if(showMenu)
{
//Bear tower
GUI.Button(new Rect(x + 10, y - 25, 50, 50), towers[0]);
//Seal tower
GUI.Button(new Rect(x + 10, y + 25, 50, 50), towers[1]);
}
}
public void ShowMenu(Vector2 pos)
{
showMenu = true;
x = pos.x;
y = pos.y;
}
}
Hope anyone can help me :)
Sry, i cant comment because i dont have enough rep, this is a comment to Steven Mills answer and comments to that post.
The first error comes because you are calling WorldToViewportPoint as if it was a static member function which it isnt(Think of if you had 2 Cameras you would have to specify which Camera you want, reading up on what a static member is would be helpful here). What you need to do to fix this is get a reference of your MainCamera and call the function from that instance(best method would probably be with a public variable and dragging the camera on the script in the editor)
The second error occurs because you are trying to give a Vector3 to ShowMenu() which requires a Vector2. The third is probably a product of the compiler trying to fix error 2
This is a logical error because the tiles are GameObjects and thus the transform.position are positions in 3d space. What you actually want is the 2d-pixel-position on the screen of your MainCamera. To get this you need to call WorldToScreenPoint instead of WorldToViewportPoint. Sadly, as you will notice you will also get a Vector3 here which is not what you want, but you can just take the x and y coordinates as your pixel screen coordinates. the z-coordinate denotes the distance from the camera.
Hope that clears it up a little instead of confusing you ;D
Feel free to ask again, but also try to read the Unity Script Reference and try to understand what is written there and what it means :)
By the look of it, your ShowMenu method is receiving a pos of (0,0), which is why the two buttons are placed at what seems to be a position of (10,-25) and (10,25) respectively.
Without seeing the code calling ShowMenu I can't be sure what location you're giving, but my guess would be that the tiles belong to a parent object, and you're passing the local position instead of the world position.
If you post the code in which you call ShowMenu I may be able to point out any problems.
EDIT:
Based on the information provided in the comments, the problem is that the position needs converting between the world coordinates and screen coordinates:
void OnMouseDown()
{
if(state == State.water)
{
errorHandler.sendError("You can't click on that");
}
if(state == State.ice)
{
towerMenu.ShowMenu(camera.WorldToViewportPoint(this.transform.position));
}
}
and change the ShowMenu to this:
public void ShowMenu(Vector3 pos)
{
showMenu = true;
x = pos.x;
y = pos.y;
}
I have a problem in the game I wrote with XNA. I recently added Textured polygons, and saw that every textured polygon shared the same texture although I changed it before calling. The code I am using:
if (countMeshes > 0)
{
for (int i = 0; i < countMeshes; i++)
{
TexturedMesh curMesh = listMeshes[i];
if (curMesh.tex == null)
{
drawEffect.Texture = WHITE_TEXTURE;
}
else
{
drawEffect.Texture = curMesh.tex;
}
drawEffect.Techniques[0].Passes[0].Apply();
graphics.DrawUserPrimitives(PrimitiveType.TriangleList, curMesh.verts, 0, curMesh.count);
}
}
Now, the first that came into my mind would be to create a BasicEffect for each Texture I need to draw. But I think that would be a bit of a overkill so my question is: How should I do it?
PS: I double checked everything, the UV Coords are fine, and it is 2D.
It seems to be the only way, the way I did it was to create a Dictionary with the texture as the key and a struct of a basic effect and a list of Vertexs as the value.
Something like this:
public struct MeshEffect
{
public TexturedMesh mesh;
public BasicEffect effect;
}
and the Dictionary:
private Dictionary<Texture2D, MeshEffect> texturedMeshes = new Dictionary<Texture2D, MeshEffect>();
But it all really depends on how you handle drawing, but one thing is sure, you can't use more than one texture on one BasicEffect.