Trying center a tower on tilemap - c#

Iam making a placement system using tilemaps, its almost done but have a small problem.
Placement System
As you can see the tower never be on middle of tiles, i want it always be on middle, even if the size is 2x2 (on that video is 3x3), i dont know what is wrong with my code, so i need help.
void Update()
{
Vector3 mousePosWorldPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3Int cellPos = sniperTower.World.MapGrid.LocalToCell(mousePosWorldPoint);
Vector3 posConverted = sniperTower.World.MapGrid.CellToLocalInterpolated(cellPos);
BoundsInt bounds = new BoundsInt();
bounds.position = cellPos;
bounds.size = sniperTower.BuldingSize;
if (sniperTower.PreviousBounds.position != null && sniperTower.PreviousBounds.position != bounds.position) {
sniperTower.World.clearTiles(sniperTower.PreviousBounds);
sniperTower.Moving(bounds,new Vector3(posConverted.x, posConverted.y, 10));
sniperTower.World.fillTiles("Placing",bounds);
}else if(sniperTower.PreviousBounds.position == null) {
sniperTower.Moving(bounds,new Vector3(posConverted.x, posConverted.y, 10));
sniperTower.World.fillTiles("Placing",bounds);
}
}
//The Moving Method
public virtual void Moving(BoundsInt bounds,Vector3 position){
this.movingBounds = bounds;
this.previousBounds = this.movingBounds;
this.buildingObject.transform.position = position;
}
//The FillTiles and ClearTiles Methods
public void fillTiles(string tileType, BoundsInt area){
for (int x = area.x; x < (area.x + area.size.x); x++) {
for (int y = area.y; y < (area.y + area.size.y); y++) {
placingTilemap.SetTile(new Vector3Int(x,y,0),tiles[tileType]);
}
}
}
public void clearTiles(BoundsInt area){
for (int x = area.x; x < (area.x + area.size.x); x++) {
for (int y = area.y; y < (area.y + area.size.y); y++) {
placingTilemap.SetTile(new Vector3Int(x,y,0),null);
}
}
}

Related

Unity - Physics.CheckSphere problem with Masks

I am having trouble with Unity Physics.CheckSphere. I have added a mask "Wall" and assigned it to the object.
In my custom editor window I can select this mask and set values as a grid to check for this object. However, when I click the button I don't get any collisions. But when I change the object's mask to something like "TransparentFX" I get the results I expected.
What am I missing here?
Here is the code for the custom editor window:
private void OnGUI()
{
GUILayout.Label("Generate A*", EditorStyles.boldLabel);
showGridTools = EditorGUILayout.BeginFoldoutHeaderGroup(showGridTools, "Grid Tools");
if(showGridTools)
{
this.GridDimensions = EditorGUILayout.Vector3IntField("Grid Dimensions", GridDimensions);
this.ObstacleMask = EditorGUILayout.LayerField("Obstacle Mask", this.ObstacleMask);
if (GUILayout.Button("Generate Grid"))
CreateGrid();
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
And for checking the collisions:
private void CreateGrid()
{
for (int x = 0; x < this.GridDimensions.x; x++)
{
for (int z = 0; z < this.GridDimensions.z; z++)
{
for (int y = 0; y < this.GridDimensions.y; y++)
{
Vector3Int position = new Vector3Int(x, y, z);
bool obstacle = Physics.CheckSphere(position, 0.3f, this.ObstacleMask);
if(obstacle)
Debug.Log($"Position: {position}, Obstacle: {obstacle}");
}
}
}
}
----------- EDIT -----------
I just did this. Might be pretty slow, I don't know, but at least it
works for now.
private void CreateGrid()
{
this.Grid = new Node[this.GridDimensions.x, this.GridDimensions.y, this.GridDimensions.z];
for (int x = 0; x < this.GridDimensions.x; x++)
{
for (int z = 0; z < this.GridDimensions.z; z++)
{
for (int y = 0; y < this.GridDimensions.y; y++)
{
Vector3Int position = new Vector3Int(x, y, z);
var collisions = Physics.OverlapSphere(position, 0.3f);
bool obstacle = collisions.Where(c => c.gameObject.layer == this.ObstacleMask).Count() > 0;
if (obstacle)
Debug.Log($"Position: {position}, Obstacle: {obstacle}");
}
}
}
}

Issues with Steering Behavior Seperation

The problem
I am trying to procedurally generate dungeon rooms with random X, Y sizes inside of a radius (r). However, even after I validate that the starting grid (origin of the "room") is not in the same position as other origins after running the separation function there are rooms still building inside of each other.
Solutions I have tried
I tried using math to calculate an optimal radius that will be able to fit the average of all the room sizes * amount of rooms. However, the separation should hypothetically work with any radius (though I want to keep them relatively close in order to keep hallways short).
Code
All my code is based on one tile. This means that all calculations are using one tile, and will remain one tile until the very end, then I scale them up.
private void GenerateRooms(int amount)
{
// init sizes
Vector2[] room_sizes = new Vector2[amount];
for (int i = 0; i < amount; i++)
{
room_sizes[i] = new Vector2(Random.Range(minimum_room_height, maximum_room_height), Random.Range(minimum_room_width, maximum_room_width));
}
float biggest_room = calculations.CalculateBiggest(room_sizes);
Vector2[] room_points = new Vector2[amount];
Vector2[] used_points = new Vector2[amount];
float radius = calculations.CalculateAverage(room_sizes) * amount;
for (int i = 0; i < amount; i++)
{
do {
Vector2 test_point = new Vector2(Random.Range(-radius, radius), Random.Range(-radius, radius));
foreach (Vector2 point in used_points) {
if (test_point == point) {
continue;
} else {
room_points[i] = test_point;
used_points[i] = test_point;
break;
}
}
} while (Vector2.Distance(Vector2.zero, room_points[i]) < radius);
}
for (int i = 0; i < amount; i++)
{
//Vector2 origin = room_points[i];
Vector3 position = calculations.computeSeperate(room_points, room_points[i], biggest_room);
//position = new Vector3(position.x + origin.x, position.y + origin.y, 0);
Vector3Int location = tile_map.WorldToCell(position);
tile_map.SetTile(location, tile);
calculations.scaleUpRooms(position, room_sizes[i].x, room_sizes[i].y, tile_map, tile);
}
}
The above is code for calling all the functions and validating the points. Here are the important functions (calculation functions):
public Vector2 computeSeperate(Vector2[] point_array, Vector2 target_point, float minimum_distance)
{
int neighbor_count = 0;
for (int i = 0; i < point_array.Length; i++)
{
if (point_array[i] != target_point)
{
if (Vector2.Distance(target_point, point_array[i]) < minimum_distance * 2)
{
target_point.x += point_array[i].x - target_point.x;
target_point.y += point_array[i].y - target_point.y;
neighbor_count++;
}
}
}
if (neighbor_count == 0)
{
return target_point;
} else
{
target_point.x /= neighbor_count;
target_point.y /= neighbor_count;
target_point.x *= -1;
target_point.y *= -1;
target_point.Normalize();
return target_point;
}
}
public void scaleUpRooms(Vector2 base_point, float scale_x, float scale_y, Tilemap tile_map, Tile tile) // ex: 5x5
{
List<Vector2> Calculate(Vector2 size)
{
List<Vector2> results = new List<Vector2>();
for (int i = 0; i < size.y; i++)
for (int o = 0; o < size.x; o++)
results.Add(new Vector2(o, i) + (new Vector2(size.x % 2 != 0 ? .5f : 1, size.y % 2 != 0 ? .5f : 1) - (size / 2)));
string st = "";
for (int i = 0; i < results.Count; i++)
st += "\n" + results[i].ToString();
return results;
}
Vector2 desired_scale = new Vector2(scale_x, scale_y);
List<Vector2> Offsets = Calculate(desired_scale);
for (int i = 0; i < Offsets.Count; i++)
{
Vector3 position = base_point + Offsets[i];
Vector3Int location = tile_map.WorldToCell(position);
tile_map.SetTile(location, tile);
}
}

Why does the array I pass to my multithreading job struct act as a reference type?

I'm working on a unity project involving deformable terrain based on marching-cubes. It works by generating a map of density over the 3-dimensional coordinates of a terrain chunk and using that data to create a mesh representing the surface of the terrain. It has been working, however the process is very slow. I'm attempting to introduce multithreading to improve performance, but I've run into a problem that's left me scratching my head.
When I run CreateMeshData() and try to pass my density map terrainMap into the MarchCubeJob struct, it recognizes it as a reference type, not a value type. I've seemed to whittle down the errors to this one, but I've tried to introduce the data in every way I know how and I'm stumped. I thought passing a reference like this was supposed to create a copy of the data disconnected from the reference, but my understanding must be flawed. My goal is to pass each marchingcube cube into a job and have them run concurrently.
I'm brand new to multithreading, so I've probably made some newbie mistakes here and I'd appreciate if someone would help me out with a second look. Cheers!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
public class Chunk
{
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
public GameObject chunkObject;
MeshFilter meshFilter;
MeshCollider meshCollider;
MeshRenderer meshRenderer;
Vector3Int chunkPosition;
public float[,,] terrainMap;
// Job system
NativeList<Vector3> marchVerts;
NativeList<Vector3> marchTris;
MarchCubeJob instanceMarchCube;
JobHandle instanceJobHandle;
int width { get { return Terrain_Data.chunkWidth;}}
int height { get { return Terrain_Data.chunkHeight;}}
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Chunk (Vector3Int _position){ // Constructor
chunkObject = new GameObject();
chunkObject.name = string.Format("Chunk x{0}, y{1}, z{2}", _position.x, _position.y, _position.z);
chunkPosition = _position;
chunkObject.transform.position = chunkPosition;
meshRenderer = chunkObject.AddComponent<MeshRenderer>();
meshFilter = chunkObject.AddComponent<MeshFilter>();
meshCollider = chunkObject.AddComponent<MeshCollider>();
chunkObject.transform.tag = "Terrain";
terrainMap = new float[width + 1, height + 1, width + 1]; // Weight of each point
meshRenderer.material = Resources.Load<Material>("Materials/Terrain");
// Generate chunk
PopulateTerrainMap();
CreateMeshData();
}
void PopulateTerrainMap(){
...
}
void CreateMeshData(){
ClearMeshData();
vertices = new List<Vector3>();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
for (int z = 0; z < width; z++) {
Debug.Log(x + ", " + y + ", " + z + ", begin");
Vector3Int position = new Vector3Int(x, y, z);
// Set up memory pointers
NativeList<Vector3> marchVerts = new NativeList<Vector3>(Allocator.TempJob);
NativeList<int> marchTris = new NativeList<int>(Allocator.TempJob);
NativeList<float> mapSample = new NativeList<float>(Allocator.TempJob);
// Split marchcube into jobs by cube
instanceMarchCube = new MarchCubeJob(){
position = position,
marchVerts = marchVerts,
marchTris = marchTris,
mapSample = terrainMap
};
// Run job for each cube in a chunk
instanceJobHandle = instanceMarchCube.Schedule();
instanceJobHandle.Complete();
// Copy data from job to mesh data
//instanceMarchCube.marchVerts.CopyTo(vertices);
vertices.AddRange(marchVerts);
triangles.AddRange(marchTris);
// Dispose of memory pointers
marchVerts.Dispose();
marchTris.Dispose();
mapSample.Dispose();
Debug.Log(x + ", " + y + ", " + z + ", end");
}
}
}
BuildMesh();
}
public void PlaceTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
public void RemoveTerrain (Vector3 pos, int radius, float speed){
...
CreateMeshData();
}
void ClearMeshData(){
vertices.Clear();
triangles.Clear();
}
void BuildMesh(){
Mesh mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.RecalculateNormals();
meshFilter.mesh = mesh;
meshCollider.sharedMesh = mesh;
}
private void OnDestroy(){
marchVerts.Dispose();
marchTris.Dispose();
}
}
// Build a cube as a job
[BurstCompile]
public struct MarchCubeJob: IJob{
static float terrainSurface { get { return Terrain_Data.terrainSurface;}}
public Vector3Int position;
public NativeList<Vector3> marchVerts;
public NativeList<int> marchTris;
public float[,,] mapSample;
public void Execute(){
//Sample terrain values at each corner of cube
float[] cube = new float[8];
for (int i = 0; i < 8; i++){
cube[i] = SampleTerrain(position + Terrain_Data.CornerTable[i]);
}
int configIndex = GetCubeConfiguration(cube);
// If done (-1 means there are no more vertices)
if (configIndex == 0 || configIndex == 255){
return;
}
int edgeIndex = 0;
for (int i = 0; i < 5; i++){ // Triangles
for (int p = 0; p < 3; p++){ // Tri Vertices
int indice = Terrain_Data.TriangleTable[configIndex, edgeIndex];
if (indice == -1){
return;
}
// Get 2 points of edge
Vector3 vert1 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 0]];
Vector3 vert2 = position + Terrain_Data.CornerTable[Terrain_Data.EdgeIndexes[indice, 1]];
Vector3 vertPosition;
// Smooth terrain
// Sample terrain values at either end of current edge
float vert1Sample = cube[Terrain_Data.EdgeIndexes[indice, 0]];
float vert2Sample = cube[Terrain_Data.EdgeIndexes[indice, 1]];
// Calculate difference between terrain values
float difference = vert2Sample - vert1Sample;
if (difference == 0){
difference = terrainSurface;
}
else{
difference = (terrainSurface - vert1Sample) / difference;
}
vertPosition = vert1 + ((vert2 - vert1) * difference);
marchVerts.Add(vertPosition);
marchTris.Add(marchVerts.Length - 1);
edgeIndex++;
}
}
}
static int GetCubeConfiguration(float[] cube){
int configurationIndex = 0;
for (int i = 0; i < 8; i++){
if (cube[i] > terrainSurface){
configurationIndex |= 1 << i;
}
}
return configurationIndex;
}
public float SampleTerrain(Vector3Int point){
return mapSample[point.x, point.y, point.z];
}
}

NullReferenceException: Object reference not set to an instance of an object when calling transform.localScale [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 2 years ago.
I have a 2D array of gameObjects pieces. When I want to rescale them in my private void ScalePieces() method, I get a NullReferenceException. If I instead just use Debug.Log() I dont get an Error. This is the whole code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PieceDrawer : MonoBehaviour
{
public string[,] BoardArray = new string[8, 8];
public GameObject[,] pieces = new GameObject[8, 8];
private bool setup = false;
public float scale = 0.7f;
private (int, int) WhiteKing = (0, 0);
private (int, int) BlackKing = (0, 0);
public GameObject bishopBlack, bishopWhite, kingBlack, kingWhite, knightBlack, knightWhite;
public GameObject pawnBlack, pawnWhite, queenBlack, queenWhite, rookBlack, rookWhite;
private Dictionary<string, GameObject> PrefabPiece = new Dictionary<string, GameObject>();
public string fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
void Start()
{
PrefabPiece["b_b"] = bishopBlack;
PrefabPiece["b_w"] = bishopWhite;
PrefabPiece["k_b"] = kingBlack;
PrefabPiece["k_w"] = kingWhite;
PrefabPiece["n_b"] = knightBlack;
PrefabPiece["n_w"] = knightWhite;
PrefabPiece["p_b"] = pawnBlack;
PrefabPiece["p_w"] = pawnWhite;
PrefabPiece["q_b"] = queenBlack;
PrefabPiece["q_w"] = queenWhite;
PrefabPiece["r_b"] = rookBlack;
PrefabPiece["r_w"] = rookWhite;
}
private void Update()
{
if (fen != "" && !setup)
{
SetupPieces();
setup = true;
}
}
private void SetupPieces()
{
var x = 0;
var y = 0;
var fenList = fen.Split();
foreach (var position in fenList[0])
{
if (position == '/')
{
x = 0;
y++;
}
else if (char.IsDigit(position))
{
for (int i = 0; i < (position - '0'); i++)
{
BoardArray[x + i, y] = " ";
}
x += position - '0';
}
else
{
switch (position)
{
case 'k':
BlackKing = (x, y);
break;
case 'K':
WhiteKing = (x, y);
break;
}
var clr = char.IsUpper(position) ? "_w" : "_b";
BoardArray[x, y] = position.ToString().ToLower() + clr;
x++;
}
}
DrawPieces();
}
private void DrawPieces()
{
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
if (BoardArray[x, y] != " ")
{
pieces[x, y] = Instantiate (PrefabPiece[BoardArray[x, y]], new Vector3 (x, y, 0), Quaternion.identity);
pieces[x, y].GetComponent<SpriteRenderer>().sortingOrder = 1;
ScalePieces();
}
}
}
}
private void ScalePieces()
{
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
// Debug.Log("Hello");
Vector3 scaleVec = new Vector3(scale, scale, 1);
pieces[x, y].transform.localScale = scaleVec;
}
}
}
}
And the error message I get in unity:
NullReferenceException: Object reference not set to an instance of an object
PieceDrawer.ScalePieces () (at Assets/PieceDrawer.cs:118)
PieceDrawer.DrawPieces () (at Assets/PieceDrawer.cs:104)
PieceDrawer.SetupPieces () (at Assets/PieceDrawer.cs:91)
PieceDrawer.Update () (at Assets/PieceDrawer.cs:44)
As I said changing the method to something else fixes the error, so I think it definetly has to do with transform.localScale. I also tried calling the method from other methods; same error.
ScalePieces is called inside the loop in DrawPieces when not all entries of pieces[] are set. Better call it after the loop.

Unity: Error CS1041 on line 5: Identifier expected: 'public' is a keyword?

I have been following the Unity3D Procedural Cave Generation, but I found an error very early on in MapGeneration.cs. Unity says that on line 1 word 1, there is an error: Identifier expected: 'public' is a keyword. I cannot see any difference from my code and the tutorial's code. Here is the link to the tutorial video: [\Tutorial video 1] and here is my code:
using UnityEngine;
using System.Collections;
using System
public class MapGeneration : MonoBehaviour {
public int width;
public int height;
public string seed;
public bool useRandomSeed;
[Range(0,100)]
public int randomFillPercent;
int[,] map;
void Start() {
GenerateMap();
}
void GenerateMap() {
map = new int[width,height];
}
void RandomFillMap() {
if (useRandomSeed) {
seed = Time.time.ToString();
}
System.Random psuedoRandom = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y ++) {
map[x,y] = (psuedoRandom.Next(0,100) < randomFillPercent)? 1: 0;
}
}
}
void OnDrawGizmos() {
if (map != null) {
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y ++) {
Gizmos.color = (map[x,y] == 1)? Color.black: Color.white;
Vector3 position = new Vector3(-width/2 + x + .5f,0,-height/2 + y + .5f);
Gizmos.DrawCube(position,Vector3.one);
}
}
}
}
}
The error is public on line one.
You don't have the ; after using System (that, maybe, is also an incomplete import).

Categories