I need Help, I'm doing a smaller puzzle game but I have trouble getting the playing field to the center.
Playing field is still on the bottom/left
I tried to fix it using the forum but nothing happened.
Thanks for your help.
Problem
GameBoard.cs
using UnityEngine;
using System.Collections;
public class GameBoard : MonoBehaviour {
public int m_size;
public GameObject m_puzzlePiece;
public GameObject buttonNext;
private PuzzleSection[,] m_puzzle;
private PuzzleSection m_puzzleSelection;
public int m_randomPasses = 12;
void Start()
{
GameObject temp;
m_puzzle = new PuzzleSection[m_size, m_size];
for (int i=0; i< m_size; i++)
{
for (int j=0; j< m_size; j++)
{
temp = (GameObject)Instantiate(m_puzzlePiece,
new Vector2(i*700/m_size, j*700/m_size), Quaternion.identity);
temp.transform.SetParent(transform);
m_puzzle[i, j] = (PuzzleSection)temp.GetComponent<PuzzleSection>();
m_puzzle[i, j].CreatePuzzlePiece(m_size);
}
}
SetupBoard();
RandomizePlacement();
}
void RandomizePlacement()
{
VectorInt2[] puzzleLocation = new VectorInt2[2];
Vector2[] puzzleOffset = new Vector2[2];
do
{
for (int i = 0; i < m_randomPasses; i++)
{
puzzleLocation[0].x = Random.Range(0, m_size);
puzzleLocation[0].y = Random.Range(0, m_size);
puzzleLocation[1].x = Random.Range(0, m_size);
puzzleLocation[1].y = Random.Range(0, m_size);
puzzleOffset[0] = m_puzzle[puzzleLocation[0].x, puzzleLocation[0].y].GetImageOffset();
puzzleOffset[1] = m_puzzle[puzzleLocation[1].x, puzzleLocation[1].y].GetImageOffset();
m_puzzle[puzzleLocation[0].x, puzzleLocation[0].y].AssignImage(puzzleOffset[1]);
m_puzzle[puzzleLocation[1].x, puzzleLocation[1].y].AssignImage(puzzleOffset[0]);
}
} while (CheckBoard() == true);
}
void SetupBoard()
{
Vector2 offset;
Vector2 m_scale = new Vector2(1f / m_size, 1f / m_size);
for (int i=0; i< m_size; i++)
{
for (int j=0; j< m_size; j++)
{
offset = new Vector2(i * (1f / m_size), j * (1f / m_size));
m_puzzle[i, j].AssignImage(m_scale, offset);
}
}
}
public PuzzleSection GetSelection()
{
return m_puzzleSelection;
}
public void SetSelection(PuzzleSection selection)
{
m_puzzleSelection = selection;
}
public bool CheckBoard()
{
for (int i=0; i<m_size; i++)
{
for(int j=0; j< m_size; j++)
{
if (m_puzzle[i, j].CheckGoodPlacement() == false)
return false;
}
}
return true;
}
public void Win()
{
buttonNext.SetActive(true);
}
}
PuzzleSection.cs
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class PuzzleSection : MonoBehaviour {
Vector2 m_goodOffset;
Vector2 m_offset;
Vector2 m_scale;
GameBoard m_gameBoard;
void Start()
{
m_gameBoard = (GameBoard)GameObject.FindObjectOfType<Canvas>()
.GetComponentInChildren<GameBoard>();
}
public void CreatePuzzlePiece(int size)
{
transform.localScale = new Vector3(1.0f * transform.localScale.x / size,
1.0f * transform.localScale.z / size, 1);
}
public void AssignImage(Vector2 scale, Vector2 offset)
{
m_goodOffset = offset;
m_scale = scale;
AssignImage(offset);
}
public void AssignImage(Vector2 offset)
{
m_offset = offset;
GetComponent<RawImage>().uvRect = new Rect(offset.x, offset.y, m_scale.x, m_scale.y);
}
public void OnClick()
{
PuzzleSection previousSelection = m_gameBoard.GetSelection();
if (previousSelection != null)
{
previousSelection.GetComponent<RawImage>().color = Color.white;
Vector2 tempOffset = previousSelection.GetImageOffset();
previousSelection.AssignImage(m_offset);
AssignImage(tempOffset);
m_gameBoard.SetSelection(null);
if (m_gameBoard.CheckBoard() == true)
m_gameBoard.Win();
} else
{
GetComponent<RawImage>().color = Color.gray;
m_gameBoard.SetSelection(this);
}
}
public Vector2 GetImageOffset()
{
return m_offset;
}
public bool CheckGoodPlacement()
{
return (m_goodOffset == m_offset);
}
}
Hey guys, I need Help, I'm doing a smaller puzzle game but I have trouble getting the playing field to the center.
Playing field is still on the bottom/left
I tried to fix it using the forum but nothing happened.
Thanks for your help.
As i gather from the picture your gameboard gets drawn inside a Panel ui control.
You could set the position of this panel to the centre, so that all its children will be centred alongside it:
MyPanel.transform.position = new Vector3 (Screen.width * 0.5f, Screen.height * 0.5f, 0);
Related
The enemy will pick 1 or 2 positions, and then stop moving. If I get close enough, it will change to the chase state and follow me. So I do not know what causes them to freeze.
This holds the state machine, I intend to have more states and interactions, but I wanted to test the basic functionality, and it is broken.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseAI : MonoBehaviour
{
private enum State
{
Roaming,
ChaseTarget,
}
private PathfindAI pathMovement;
private Vector3 startingPosition;
public Vector3 roamPosition;
public PlayerScript player;
public Vector3 randomDirection;
[SerializeField] private State state;
public Vector3 playerPos;
private void Awake()
{
// pathMovement = GetComponent<PathfindAI>();
pathMovement = FindObjectOfType<PathfindAI>();
player = FindObjectOfType<PlayerScript>();
state = State.Roaming;
}
private void Start()
{
startingPosition = transform.position;
roamPosition = GetRoamingPos();
}
private void Update()
{
playerPos = player.PlayerPos();
switch (state)
{
default:
case State.Roaming:
pathMovement.MoveTo(roamPosition);
float reachedPos = 10f;
if (Vector3.Distance(transform.position, roamPosition) < reachedPos)
{
//reached destination
roamPosition = GetRoamingPos();
}
FindTarget();
break;
case State.ChaseTarget:
pathMovement.MoveTo(playerPos);
float attackRange = 10f;
if (Vector3.Distance(transform.position, playerPos) < attackRange)
{
//target within striking distance
}
break;
}
}
private Vector3 GetRoamingPos()
{
randomDirection = new Vector3(Random.Range(-1f, 1f), Random.Range(-1f, 1f), transform.position.z).normalized;
return startingPosition + randomDirection * Random.Range(10f, 70f);
}
private void FindTarget()
{
float targetRange = 50f;
if (Vector3.Distance(transform.position, playerPos) < targetRange)
{
state = State.ChaseTarget;
}
}
}
This allows them to move on the path from the pathfinding script. It uses A-star and I used a few tutorials to write it, mostly from CodeMonkey's youtube channel.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathfindAI : MonoBehaviour
{
[SerializeField] float speed = 5f;
private int currentPathIndex;
private List<Vector3> pathVectorList;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Debug.Log(currentPathIndex);
}
public Vector3 GetPosition()
{
return transform.position;
}
public void MoveTo(Vector3 targetPosition)
{
SetTargetPos(targetPosition);
HandleMove();
}
private void SetTargetPos(Vector3 targetPosition)
{
currentPathIndex = 0;
pathVectorList = AStar.Instance.FindPathVector3(GetPosition(), targetPosition);
if (pathVectorList != null && pathVectorList.Count > 1)
{
pathVectorList.RemoveAt(0);
}
}
public void HandleMove()
{
if (pathVectorList != null)
{
Vector3 targetPosition = pathVectorList[currentPathIndex];
if(Vector3.Distance(transform.position, targetPosition) > 5f)
{
Vector3 moveDir = (targetPosition - transform.position).normalized;
float distanceBefore = Vector3.Distance(transform.position, targetPosition);
transform.position = transform.position + moveDir * speed * Time.deltaTime;
}
else
{
currentPathIndex++;
if(currentPathIndex >= pathVectorList.Count)
{
StopMoving();
}
}
}
}
private void StopMoving()
{
pathVectorList = null;
}
}
I suspect the error involves the HandleMove function. When I change where and how I call it; I can get it to break completely. It may be me setting the pathVectorList to null; however, it should be set back to a valid value on the next update when GetRoamingPos is called again within the roaming state.
when I go into the inspector and manually move the npc, it will find its way back to the position it chose. And if I move the player close enough it will chase me.
This is my A-Star Implementation
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AStar
{
private const int MOVE_STRAIGHT_COST = 10;
private const int MOVE_ANGLE_COST = 14;
public static AStar Instance { get; private set; }
private PathingGrid<PathNode> grid;
private List<PathNode> openList;
private List<PathNode> closedList;
public AStar(int width, int height)
{
Instance = this;
grid = new PathingGrid<PathNode>(width, height, 10f, Vector3.zero, (PathingGrid<PathNode> g, int x, int y) => new PathNode(g, x, y));
}
public PathingGrid<PathNode> GetGrid()
{
return grid;
}
public List<PathNode> FindPath(int startX, int startY, int endX, int endY)
{
PathNode startNode = grid.GetGridObject(startX, startY);
PathNode endNode = grid.GetGridObject(endX, endY);
openList = new List<PathNode> { startNode };
closedList = new List<PathNode>();
for (int x = 0; x<grid.GetWidth(); x++)
{
for (int y = 0; y<grid.GetHeight(); y++)
{
PathNode pathNode = grid.GetGridObject(x, y);
pathNode.gCost = int.MaxValue;
pathNode.CalcFCost();
pathNode.cameFromNode = null;
}
}
startNode.gCost = 0;
startNode.hCost = CalculateDistanceCost(startNode, endNode);
startNode.CalcFCost();
while (openList.Count > 0)
{
PathNode currentNode = GetLowestFCost(openList);
if (currentNode == endNode)
{
//reached final node
return CalculatedPath(endNode);
}
openList.Remove(currentNode);
closedList.Add(currentNode);
foreach (PathNode neighbourNode in GetNeighboursList(currentNode))
{
if (closedList.Contains(neighbourNode))
{
continue;
}
if (!neighbourNode.isWalkable)
{
closedList.Add(neighbourNode);
continue;
}
int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNode, neighbourNode);
if (tentativeGCost < neighbourNode.gCost)
{
neighbourNode.cameFromNode = currentNode;
neighbourNode.gCost = tentativeGCost;
neighbourNode.hCost = CalculateDistanceCost(neighbourNode, endNode);
neighbourNode.CalcFCost();
if(!openList.Contains(neighbourNode))
{
openList.Add(neighbourNode);
}
}
}
}
//out of nodes on openList
return null;
}
private List<PathNode> GetNeighboursList(PathNode currentNode)
{
List<PathNode> neighbourList = new List<PathNode>();
if (currentNode.x - 1 >= 0)
{
//left
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y));
//left down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y - 1));
//left up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x - 1, currentNode.y + 1));
}
if (currentNode.x + 1 < grid.GetWidth())
{
//right
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y));
//right down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y - 1));
//right up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x + 1, currentNode.y + 1));
}
//down
if (currentNode.y - 1 >= 0)
neighbourList.Add(GetNode(currentNode.x, currentNode.y - 1));
//up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(GetNode(currentNode.x, currentNode.y + 1));
return neighbourList;
}
public PathNode GetNode(int x, int y)
{
return grid.GetGridObject(x, y);
}
public List<Vector3> FindPathVector3(Vector3 startWorldPosition, Vector3 endWorldPosition)
{
grid.GetXY(startWorldPosition, out int startX, out int startY);
grid.GetXY(endWorldPosition, out int endX, out int endY);
List<PathNode> path = FindPath(startX, startY, endX, endY);
if (path == null)
{
return null;
}
else
{
List<Vector3> vectorPath = new List<Vector3>();
foreach(PathNode pathNode in path)
{
vectorPath.Add(new Vector3(pathNode.x, pathNode.y) * grid.GetCellSize() + Vector3.one * grid.GetCellSize() * .5f);
}
return vectorPath;
}
}
private List<PathNode> CalculatedPath(PathNode endNode)
{
List<PathNode> path = new List<PathNode>();
path.Add(endNode);
PathNode currentNode = endNode;
while (currentNode.cameFromNode != null)
{
path.Add(currentNode.cameFromNode);
currentNode = currentNode.cameFromNode;
}
path.Reverse();
return path;
}
private int CalculateDistanceCost(PathNode a,PathNode b)
{
int xDistance = Mathf.Abs(a.x - b.x);
int yDistance = Mathf.Abs(a.y - b.y);
int remaining = Mathf.Abs(xDistance - yDistance);
return MOVE_ANGLE_COST * Mathf.Min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining;
}
private PathNode GetLowestFCost(List<PathNode> pathNodeList)
{
PathNode lowestFCostNode = pathNodeList[0];
for ( int i = 1; i < pathNodeList.Count; i++)
{
if (pathNodeList[i].fCost < lowestFCostNode.fCost)
{
lowestFCostNode = pathNodeList[i];
}
}
return lowestFCostNode;
}
}
One interesting point to note is that whenever my random point is chosen outside of the grid I have made, the error is not thrown where I would expect. Instead they claim that in my calculateDistance Cost"int xDistance = Mathf.Abs(a.x - b.x);" is not Finding an instanced Pathnode. Which makes sense because there wouldn't be one there. However, I would not expect that to be where the first error occurs with an out of bounds endpoint.
I have some code that is supposed to create a grid of voxels, and then rooms out of those voxels. However, a lot of the rooms still overlap, even when I wrote code specifically to prevent this from happening. Would anybody be able to tell me what's going?
I'm using an out variable for the first time, and I think that's part of the issues.
Basically, the code is supposed to create a list of voxels to turn into a room, turn them into a room, and repeat for the total number of rooms. If a voxel in the list is found to already be a room, it should clear the list and not create a room.
All the relevant code is below, you should be able to plug this into a new Unity project and see for yourself everything, complete with Gizmos.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum VoxelType{
Undefined = 0,
OpenRoom,
OpenHall,
Wall,
Buffer,
}
[System.Serializable]
public class Voxel {
public Vector3 position;
public VoxelType voxelType;
public int voxelX;
public int voxelY;
public Voxel(Vector3 newPosition, int x, int y, VoxelType newType = 0){
position = newPosition;
voxelType = newType;
voxelX = x;
voxelY = y;
}
}
public class VoxelManager : MonoBehaviour
{
public Vector2 mapSize;
public float voxelSize;
public int rooms;
public int maxRoomHeight;
public int minRoomHeight;
public int maxRoomWidth;
public int minRoomWidth;
List<Voxel> voxels = new List<Voxel>();
void OnDrawGizmosSelected(){
foreach(Voxel voxel in voxels){
if (voxel.voxelType == VoxelType.OpenRoom){
Gizmos.color = Color.red;
} else {
Gizmos.color = Color.blue;
}
switch (voxel.voxelType){
case VoxelType.Undefined:
Gizmos.color = Color.blue;
break;
case VoxelType.Wall:
Gizmos.color = Color.magenta;
break;
case VoxelType.OpenRoom:
Gizmos.color = Color.red;
break;
}
Gizmos.DrawCube(voxel.position, Vector3.one * voxelSize *.995f);
}
}
void Start(){
voxelSpawn = prefab;
for(int i = 0; i < mapSize.x; i++){
for (int j = 0; j < mapSize.y; j++){
float xLocation = voxelSize * i;
float zLocation = voxelSize * j;
float centerXLocation = (xLocation - (mapSize.x * voxelSize) / 2f) + voxelSize / 2f;
float centerZLocation = (zLocation - (mapSize.y * voxelSize) / 2f) + voxelSize / 2f;
string voxelName = i + "x" + j;
Vector3 center = new Vector3(centerXLocation, voxelSize / 2, centerZLocation);
voxels.Add(new Voxel(center, i, j));
}
}
for (int i = 0; i < rooms; i++){
List<Voxel> roomVoxels = new List<Voxel>();
int generatedHeight = Random.Range(minRoomHeight, maxRoomHeight);
int generatedWidth = Random.Range(minRoomWidth, maxRoomWidth);
int horizontalRoomIndex = Random.Range(0, ((int)mapSize.x));
int verticalRoomIndex = Random.Range(0, ((int)mapSize.y));
bool occupied = false;
foreach(Voxel voxel in voxels){
if(voxel.voxelX == horizontalRoomIndex && voxel.voxelY == verticalRoomIndex){
for (int j = 0; j < generatedHeight; j++){
for (int k = 0; k < generatedWidth; k++){
roomVoxels.Add(GetVoxelAtPosition(horizontalRoomIndex + j, verticalRoomIndex + k, out occupied));
}
}
}
}
if(occupied == true){
Debug.Log("Room occupied; unable to create! Clearing the current list to be turned into a room!");
roomVoxels.Clear();
i--;
} else {
Debug.Log("Making a room!");
foreach(Voxel roomVoxel in roomVoxels){
roomVoxel.voxelType = VoxelType.OpenRoom;
}
}
}
}
Voxel GetVoxelAtPosition(int x, int y, out bool occupied){
foreach(Voxel voxel in voxels){
if(voxel.voxelX == x && voxel.voxelY == y){
if (voxel.voxelType == VoxelType.Undefined){
occupied = false;
return voxel;
} else {
Debug.Log("Voxel occupied!");
occupied = true;
return voxels[0];
}
}
}
occupied = true;
return voxels[0];
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
[Header("Main Settings")]
[Space(5)]
public Transform squadMemeberPrefab;
[Range(4, 100)]
public int numberOfSquadMembers = 20;
[Range(1, 20)]
public int numberOfSquads = 1;
[Range(0, 4)]
public int columns = 4;
public int gaps = 10;
public int circleRadius = 10;
public float yOffset = 0;
[Range(3, 50)]
public float moveSpeed = 3;
[Range(3, 50)]
public float rotateSpeed = 1;
public float threshold = 0.1f;
public bool randomSpeed = false;
[Range(1, 100)]
public int randSpeedMin = 1;
[Range(1, 100)]
public int randSpeedMax = 1;
public bool startRandomFormation = false;
public string currentFormation;
private Formation formation;
private List<Quaternion> quaternions = new List<Quaternion>();
private List<Vector3> newpositions = new List<Vector3>();
private bool move = false;
private bool squareFormation = false;
private List<GameObject> squadMembers = new List<GameObject>();
private float[] step;
private int[] randomSpeeds;
private int index = 0;
private int numofobjects = 0;
// Use this for initialization
void Start()
{
numofobjects = numberOfSquadMembers;
if (startRandomFormation)
{
formation = (Formation)UnityEngine.Random.Range(0, Enum.GetNames(typeof(Formation)).Length);
}
else
{
formation = Formation.Square;
}
currentFormation = formation.ToString();
ChangeFormation();
foreach (Transform child in gameObject.transform)
{
if (child.tag == "Squad Member")
squadMembers.Add(child.gameObject);
}
randomSpeeds = RandomNumbers(randSpeedMin, randSpeedMax, squadMembers.Count);
step = new float[squadMembers.Count];
}
// Update is called once per frame
void Update()
{
if (numofobjects != numberOfSquadMembers)
{
numofobjects = 0;
numofobjects = numberOfSquadMembers;
squadMembers = new List<GameObject>();
FormationSquare();
}
if (Input.GetKeyDown(KeyCode.F))
{
randomSpeeds = RandomNumbers(randSpeedMin, randSpeedMax, squadMembers.Count);
foreach (int speedV in randomSpeeds)
{
if (index == randomSpeeds.Length)
index = 0;
step[index] = speedV * Time.deltaTime;
index++;
}
ChangeFormation();
}
if (move == true)
{
MoveToNextFormation();
}
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
FormationSquare();
break;
case Formation.Circle:
FormationCircle();
break;
}
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Transform go = squadMemeberPrefab;
for (int i = 0; i < numofobjects; i++)
{
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
Vector3 pos = FormationSquarePositionCalculation(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
newpositions.Add(go.transform.position);
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / (float)numberOfSquadMembers;
for (int i = 0; i < numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Square;
}
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers[i].transform.LookAt(newpositions[i]);
if (randomSpeed == true)
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[i]);
}
else
{
squadMembers[i].transform.position =
Vector3.MoveTowards(squadMembers[i].transform.position, newpositions[i], step[0]);
}
if (Vector3.Distance(squadMembers[i].transform.position, newpositions[i]) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers[i].transform.rotation = Quaternion.Slerp(squadMembers[i].transform.rotation, quaternions[i], rotateSpeed * Time.deltaTime);
}
}
}
}
private static int[] RandomNumbers(int min, int max, int howMany)
{
int[] myNumbers = new int[howMany];
for (int i = 0; i < howMany; i++)
{
myNumbers[i] = UnityEngine.Random.Range(min, max);
}
return myNumbers;
}
}
In the constructor I'm searching for childs with the tag Squad Member.
But the List squadMembers will be empty since the script is attached to a new empty GameObject without any childs.
Then also the variable step will be empty.
Then inside the method MoveToNextFormation I'm checking if step is empty or not:
if (step.Length > 0)
step[0] = moveSpeed * Time.deltaTime;
If not checking the it will throw exception since there is nothing at index 0 it's null. But then if step is empty there will be no speed/s at all for the objects movements.
That's one problem.
I'm not sure even why in the constructor I did the part with the children and the "Squad Member" tag. I'm not creating yet any children with this tag so I'm confused about what I tried to do in the constructor.
The second problem is in this lines in the FormationSquare method:
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
But if squadMembers is empty then it will throw exception somewhere else in other places in the code. And I'm creating new objects inside the FormationSquare method that's since I'm starting by default with the FormationSquare but what if I want to start by default with the FormationCircle method ?
The idea is to start with minimum (1) number of squads and with minimum (4) number of members in the squad when starting the program. Or to start with any range between min and max. But it's all messed up.
In your case, I would separate the squad member prefab instantiation from the squad shape formatting, doing this will help you identify your bug.
For example add the following methods and use them during 'Start':
void AddSquadMember()
{
// Use this to instantiate/spawn a new game object prefab.
}
void AddSquadMember(GameObject object)
{
// Use this for game object already in the scene. (.eg the children with your tag)
}
Then on the formation methods remove the intantiate calls and just use whatever game object you have in the list.
Finally, I would toss the 'numofobjects' variable. Then use 'squadMembers.Count' instead of both 'numofobjects' and 'numberOfSquadMembers' assuming that during 'Start' you have taken care of instantiating all game objects in order to 'numberOfSquadMembers == squadMembers.Count'. That is because you might need to raise the squad with a few more members during gameplay.
In the manager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FormationsManager : MonoBehaviour
{
public Transform squadMemeberPrefab;
public int numberOfSquadMembers = 20;
public int columns = 4;
public int gaps = 10;
public Formations formations;
private int numofmembers;
// Use this for initialization
void Start()
{
numofmembers = numberOfSquadMembers;
formations.Init(numberOfSquadMembers, columns, gaps);
GenerateSquad();
}
// Update is called once per frame
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
GenerateSquad();
}
}
private void GenerateSquad()
{
Transform go = squadMemeberPrefab;
for (int i = 0; i < formations.newpositions.Count; i++)
{
go = Instantiate(squadMemeberPrefab);
go.position = formations.newpositions[i];
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
}
}
}
And the Formations script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Formations : MonoBehaviour
{
public List<Vector3> newpositions;
private int numberOfSquadMembers;
private int columns;
private int gaps;
private List<Quaternion> quaternions;
private Vector3 FormationSquarePositionCalculation(int index)
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
for (int i = 0; i < numberOfSquadMembers; i++)
{
Vector3 pos = FormationSquarePositionCalculation(i);
Vector3 position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
newpositions.Add(position);
}
}
public void Init(int numberOfSquadMembers, int columns, int gaps)
{
this.numberOfSquadMembers = numberOfSquadMembers;
this.columns = columns;
this.gaps = gaps;
FormationSquare();
}
}
What I want to do is in the FormationsManager in the Update not only just calling GenerateSquad but to add the new once to the last/next position of the existing already formation.
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
GenerateSquad();
}
}
If the value of numberOfSquadMembers is 20 first time and then I changed it to 21 add new object to the end of the formation and same if I change the value of numberOfSquadMembers for example from 20 to 19 or from 21 to 5 destroy the amount of objects from the end and keep the formation shape.
The soldiers the last line is on the right side.
So if I change the value to add more then add it to the right and if I change to less destroy from the right side. The most left line of soldiers is the first.
It is possible if you keep GameObject instances inside FormationsManager class, and then reuse them in GenerateSquad method.
In FormationsManager class, add and modify code as follows.
public GameObject squadMemeberPrefab;
List<GameObject> SquadMembers = new List<GameObject>();
void Update()
{
if (numofmembers != numberOfSquadMembers)
{
numofmembers = numberOfSquadMembers;
formations.Init(numberOfSquadMembers, columns, gaps);
GenerateSquad();
}
}
private void GenerateSquad()
{
Transform go = squadMemeberPrefab;
List<GameObject> newSquadMembers = new List<GameObject>();
int i = 0;
for (; i < formations.newpositions.Count; i++)
{
if (i < SquadMembers.Count)
go = SquadMembers[i];
else
{
go = Instantiate(squadMemeberPrefab);
newSquadMembers.Add(go);
}
go.position = formations.newpositions[i];
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
}
for (; i < SquadMembers.Count; i++)
Destroy(SquadMembers[i]);
SquadMembers = newSquadMembers;
}
However, I recommend you to consider GameObject Pool (Object Pool), which can thoroughly resolve such object recycle problem. For this purpose, you can use ClientScene.RegisterSpawnHandler. Go to this Unity Documentation page and search text "GameObject pool". You can see an example code there.
I have a game where you need to rotate clocks.I cant figure out how to rotate two or more clock arrows toghether.Arrows are not on the same clock.Arrows have diffrent rotations and they have to keep their rotation diffrences.I.E one arrow is 180 another 90 so after 90 degree turn fist one should be 270 and another one 180.Here is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MoveClock : MonoBehaviour
{
public GameObject root;
public Transform arrow;
public CircleCollider2D circleCollider;
public BoxCollider2D boxColliderTrigger;
public List<MoveClock> connectedClocks;
public GameManger gameManager;
public BoxCollider2D[] colliders;
public int n;
public BoxCollider2D[] disabledTriggers;
public bool equal;
const float initialValue = 90;
static public int k;
public float angle;
public float snapAngle;
public float dif;
public Vector3 mousepos;
public Vector3 dir;
public int[] zAngles;
void Start()
{
zAngles = new int[n];
SetAngles();
CalculateDiffrence();
}
void Update()
{
}
public void MoveArrow(out float snapAngle)
{
snapAngle = 0;
if (Input.GetMouseButton(0))
{
mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
dir = transform.position - mousepos;
angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg + initialValue;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
snapAngle = SnapAngle(angle);
SetAngleTo(angle);
}
}
private void SetAngles()
{
int angle = 360 / n;
for (int i = 0; i < n; i++)
{
zAngles[i] = angle * i;
}
}
private float SnapAngle(float target)
{
float minDiffrence = float.MaxValue;
float closest = float.MaxValue;
for (int i = 0; i < zAngles.Length; i++)
{
float diff = Mathf.Abs(zAngles[i] - target);
if (minDiffrence > diff)
{
minDiffrence = diff;
closest = zAngles[i];
}
}
return closest;
}
public void SetAngleTo(float a)
{
for (int i = 0; i < connectedClocks.Count; i++)
{
if(equal==true)
connectedClocks[i].arrow.rotation = Quaternion.AngleAxis(a, Vector3.forward);
else
connectedClocks[i].arrow.rotation = Quaternion.AngleAxis(a + connectedClocks[i].dif, Vector3.forward);
}
}
public void CalculateDiffrence()
{
for (int i = 0; i < connectedClocks.Count; i++)
{
Vector3 dir = connectedClocks[i].transform.position - transform.position;
dir = connectedClocks[i].transform.InverseTransformDirection(dir);
dif = SnapAngle(Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg+initialValue);
Debug.Log(string.Format("{0} {1}", dif, connectedClocks[i].circleCollider.name));
}
}
}
Simply put one of the object as the child of another, that will do.
A //or B
|__B // |__A
Or programmatically:
A.transform.parent = B.transform; //or
A.transform.SetParent(B.transform);
In this setting, if you rotate A any angles, B will follow and no need to calculate and keep their angle difference in code.