I'm trying to programm my own little game in Unity, I'm not new to programming but new to Unity and C#. The problem is that I wrote my own classes to handle the game map and I am trying to connect these classes to the MonoBehaviour Scripts in Unity.
I have a script called InputManager, which is supposed to handle the input via mouse and keyboard etc. and in this script I want to create an object of the class MapManager which has access to my tiles and stuff. The problem I'm getting is that I can't seem to create an instance of MapManager in InputManager, not with the common new MapManager() anyway. When I use this a NullPointer of some sort is created, I guess?
I do not get a compiling error but an error once the game is launched which is:
NullReferenceException: Object reference not set to an instance of an object
InputManager.Update () (at Assets/Scripts/InputManager.cs:55)
Thank you for your help!
!UPDATE!:
I tried using a workaround so that it does not matter if Start() or Update() is called first, meaning that I just instantiate MapManager in the first Update() call. However, when I run the program the error is still the same. My conclusion is that somehow my constructor is not working or the Monobehaviour script somehow does not allow using the standard constructor... Any ideas?
Here is my code:
InputManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class InputManager : MonoBehaviour
{
public int mapRightBorder = 10;
public int mapLeftBorder = -10;
public int mapTopBorder = 8;
public int mapBottomBorder = -4;
public double camSpeed = 0.2;
public double mouseSensitivity = 0.3;
public double scrollSensitivity = 2;
public double maxZoomIn = -3;
public double maxZoomOut = -12;
public Tilemap ground, overlays, buildings;
public Tilemap selected;
public AnimatedTile animatedTile;
private double detectionBorder;
private double leftRightCorrection;
private MapManager mapManager;
// Start is called before the first frame update
void Start()
{
detectionBorder = 50;
leftRightCorrection = 1.2;
mapManager = new MapManager();
mapManager.ground = ground;
mapManager.overlays = overlays;
mapManager.buildings = buildings;
mapManager.selected = selected;
mapManager.animatedTile = animatedTile;
Cursor.lockState = CursorLockMode.Confined;
}
// Update is called once per frame
void Update()
{
moveCam();
checkMouse();
mapManager.updateTilemaps();
}
private void moveCam()
{
double moveX = Camera.main.transform.position.x;
double moveY = Camera.main.transform.position.y;
double moveZ = Camera.main.transform.position.z;
double xPos = Input.mousePosition.x;
double yPos = Input.mousePosition.y;
double zDelta = Input.GetAxis("Mouse ScrollWheel");
if (Input.GetKey(KeyCode.LeftArrow))
{
moveX -= camSpeed * leftRightCorrection;
}
else if (xPos >= 0 && xPos < detectionBorder)
{
moveX -= camSpeed * mouseSensitivity * leftRightCorrection;
}
else if (Input.GetKey(KeyCode.RightArrow))
{
moveX += camSpeed * leftRightCorrection;
}
else if (xPos <= Screen.width && xPos > Screen.width - detectionBorder)
{
moveX += camSpeed * mouseSensitivity * leftRightCorrection;
}
if(moveX > mapRightBorder || moveX < mapLeftBorder)
{
moveX = Camera.main.transform.position.x;
}
if (Input.GetKey(KeyCode.DownArrow))
{
moveY -= camSpeed;
}
else if (yPos >= 0 && yPos < detectionBorder)
{
moveY -= camSpeed * mouseSensitivity;
}
else if (Input.GetKey(KeyCode.UpArrow))
{
moveY += camSpeed;
}
else if (yPos <= Screen.height && yPos > Screen.height - detectionBorder)
{
moveY += camSpeed * mouseSensitivity;
}
if(moveY > mapTopBorder || moveY < mapBottomBorder)
{
moveY = Camera.main.transform.position.y;
}
if (!(moveZ + zDelta * scrollSensitivity > maxZoomIn || moveZ + zDelta * scrollSensitivity < maxZoomOut))
{
moveZ += zDelta * scrollSensitivity;
}
Vector3 newPos = new Vector3((float)moveX, (float)moveY, (float) moveZ);
Camera.main.transform.position = newPos;
}
private void checkMouse()
{
if (Input.GetMouseButtonDown(0))
{
mapManager.selectTile(Input.mousePosition);
}
}
}
MapManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
public class MapManager
{
public int mapWidth = 16;
public int mapHeight = 16;
public Tilemap ground, overlays, buildings;
public Tilemap selected;
public AnimatedTile animatedTile;
private Vector3Int currentlySelected;
private OwnTilemap tilemap;
public MapManager()
{
tilemap = new OwnTilemap(mapWidth, mapHeight, ground, overlays, buildings);
currentlySelected = new Vector3Int(-1, -1, 0);
}
private Vector3Int mouseToTilemap(Vector3 mousePosition)
{
Ray ray = Camera.main.ScreenPointToRay(mousePosition);
// create a plane at 0,0,0 whose normal points to +Y:
Plane hPlane = new Plane(Vector3.forward, Vector3.zero);
// Plane.Raycast stores the distance from ray.origin to the hit point in this variable:
float enter = 0.0f;
if (hPlane.Raycast(ray, out enter))
{
//Get the point that is clicked
Vector3 hitPoint = ray.GetPoint(enter);
Vector3Int cell = ground.WorldToCell(hitPoint);
return cell;
}
return new Vector3Int(-1, -1, 0);
}
private bool isInBorders(Vector3Int p)
{
if (p.z != 0) return false;
if (p.x < 0 || p.x >= mapWidth) return false;
if (p.y < 0 || p.y >= mapHeight) return false;
return true;
}
public void updateTilemaps()
{
tilemap.updateTilemaps();
}
public void previewBuilding()
{
}
public void selectTile(Vector3 mousePosition)
{
if(currentlySelected != new Vector3Int(-1, -1, 0))
{
selected.SetTile(currentlySelected, null);
}
if (isInBorders(mouseToTilemap(mousePosition)))
{
selected.SetTile(mouseToTilemap(mousePosition), animatedTile);
currentlySelected = mouseToTilemap(mousePosition);
}
}
}
Related
I am using Sebastian Graves' Dark Souls in Unity series in order to make a game with souls-like mechanics and reached episode 4 but in comparison to his video, my player is tuck in place when using rolling or backstepping.
The problem seems to be coming from the AnimatorHandler script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Game
{
public class AnimatorHandler : MonoBehaviour
{
public Animator anim;
public InputHandler inputHandler;
public PlayerLocomotion playerLocomotion;
int vertical;
int horizontal;
public bool canRotate;
public void Initialize()
{
anim = GetComponent<Animator>();
inputHandler = GetComponentInParent<InputHandler>();
playerLocomotion = GetComponentInParent<PlayerLocomotion>();
vertical = Animator.StringToHash("Vertical");
horizontal = Animator.StringToHash("Horizontal");
}
public void UpdateAnimatorValues(float verticalMovement, float horizontalMovement)
{
float v = 0;
if (verticalMovement > 0 && verticalMovement < 0.55f)
{
v = 0.5f;
}
else if (verticalMovement > 0.55f)
{
v = 1;
}
else if (verticalMovement < 0 && verticalMovement > -0.55f)
{
v = -0.5f;
}
else if (verticalMovement < -0.55f)
{
v = -1;
}
else
{
v = 0;
}
float h = 0;
if (horizontalMovement > 0 && horizontalMovement < 0.55f)
{
h = 0.5f;
}
else if (horizontalMovement > 0.55f)
{
h = 1;
}
else if (horizontalMovement < 0 && horizontalMovement > -0.55f)
{
h = -0.5f;
}
else if (horizontalMovement < -0.55f)
{
h = -1;
}
else
{
h = 0;
}
anim.SetFloat(vertical, v, 0.1f, Time.deltaTime);
anim.SetFloat(horizontal, h, 0.1f, Time.deltaTime);
}
public void PlayTargetAnimation(string targetAnim, bool isInteracting)
{
anim.applyRootMotion = isInteracting;
anim.SetBool("isInteracting", isInteracting);
anim.CrossFade(targetAnim, 0.2f);
}
public void CanRotate()
{
canRotate = true;
}
public void StopRotation()
{
canRotate = false;
}
private void OnAnimatorMove()
{
if (inputHandler.isInteracting == false)
return;
float delta = Time.deltaTime;
playerLocomotion.rigidbody.drag = 0;
Vector3 deltaPosition = anim.deltaPosition;
deltaPosition.y = 0;
Vector3 velocity = deltaPosition / delta;
playerLocomotion.rigidbody.velocity = velocity;
// ???
}
}
}
The only method I found to make the player able to move was to delete playerLocomotion.rigidbody.velocity = velocity;
, but this generates another problem, as I am now able to change roll direction mid-animation.
How can I make this work?
I am currently trying to make more bullet hell patterns for my game, however, I am currently stuck with making a line pattern.
I'm wanting the pattern to make a line of bullets that are at different speeds to each other so the bullets spread from each other. I know I could make several prefabs with slightly different speeds but that's inefficient with space.
The way bullets are spawned is that their rotation is set and they move forward in that direction, however, I am unsure how to set the bullets speed from another class which determines the patterns.
Here is the patterns class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletPatternsModule : MonoBehaviour
{
static ObjectPools objectPooler;
void Start()
{
objectPooler = ObjectPools.Instance;
}
public static void ShootArc(float ArcSize, int BulletAmount, string BulletName, Transform tf, float Offset)//All arcs are in angles, not radians
{
float angle = 0;
angle = Offset;//Offset is to the left
for (int i = 0; i < BulletAmount; i++)
{
float AngleStep = ArcSize / BulletAmount;//Gets the step size for arc
angle += AngleStep;
objectPooler.SpawnFromPool(BulletName, tf.position, Quaternion.Euler(0, 0, angle));//Shoots the bullet
}
}
public static IEnumerator ShootArcEnum(float ArcSize, int BulletAmount, string BulletName, Transform tf, float Offset, float LengthOfTime, float WaitPeriod)//All arcs are in angles, not radians
{
float angle = 0;
angle = Offset;//Offset is to the left
for (int i = 0; i < BulletAmount; i++)
{
float AngleStep = ArcSize / BulletAmount;
angle += AngleStep;
objectPooler.SpawnFromPool(BulletName, tf.position, Quaternion.Euler(0, 0, angle));
yield return new WaitForSeconds(WaitPeriod);
}
}
//Not quite done yet, problem
public static void ShootLine(float MinVel, float MaxVel, int BulletAmount, string BulletName, Transform tf, float angle)//All arcs are in angles, not radians
{
float Difference = MaxVel - MinVel / BulletAmount;
for (int i = 0; i < BulletAmount; i++)
{
objectPooler.SpawnFromPool(BulletName, tf.position, Quaternion.Euler(0, 0, angle));//Shoots the bullet
}
}
}
and here is the Bullet class
using UnityEngine;
public class BulletAI : MonoBehaviour, IPooledObject
{
public Transform tf;
private Vector2 Movement;
public string BulletParticle;
private float speedx;
private float speedy;
[Header("subtractive speed")]
public bool ChangeAcc;
public float AccX;
public float AccY;
public float AccMinX;
public float AccMinY;
[Header("sine wave movement")]
public bool SineMove;
public float SineAmp;
public float SineFreq;
[Header("Set up stuff")]
public float speedxMem;
public float speedyMem;
private float BulletRot;
private float ST;
private int Length;
public bool Specifics;
public bool Killable;
public string[] CollisionNames;
ObjectPools objectPooler;
void Start()
{
objectPooler = ObjectPools.Instance;
speedxMem = speedx;
speedyMem = speedy;
}
// Start is called before the first frame update
public void OnObjectSpawn()
{
if (DEBUG.ChangeGraphics == true)//CHanges sprite to toast
{
SpriteRenderer rend = gameObject.GetComponent<SpriteRenderer>();
rend.sprite = Resources.Load<Sprite>("Toast");
}
if (BulletParticle == null)
{
BulletParticle = "BulletHit";
}
speedx = speedxMem;
speedy = speedyMem;
Movement = new Vector2(speedx, speedy);
ST = 0f;//Sine phase
}
void OnTriggerEnter2D(Collider2D coll)
{
if (Specifics == true)
{
Length = CollisionNames.Length;
for (int i = 0; i < Length; i++)
{
if (coll.gameObject.CompareTag(CollisionNames[i]) && Killable)
{
objectPooler.SpawnFromPool("BulletHit", tf.position, Quaternion.identity);
gameObject.SetActive(false);
}
}
}
}
void Update()
{
Movement = new Vector2(speedx, speedy);
if (ChangeAcc)
{
speedx -= AccX * Time.deltaTime;
speedy -= AccY * Time.deltaTime;
if (speedx <= -AccMinX || speedx >= AccMinX || speedy <= -AccMinY || speedy >= AccMinY)
{
objectPooler.SpawnFromPool(BulletParticle, tf.position, Quaternion.identity);
gameObject.SetActive(false);
}
}
if (SineMove)
{
ST += Time.deltaTime;
speedy = SineAmp * Mathf.Sin(ST * SineFreq);
}
if (tf.position.x > 25f || tf.position.x < -25f || tf.position.y > 15f || tf.position.x < -15f)
{
gameObject.SetActive(false);
}
}
// Update is called once per frame
void FixedUpdate()
{
tf.Translate(Movement * Time.deltaTime);
}
}
Fixed the issue by changing the speed of the bullet in the object pooler
I just need to add to it to make the character movement/camera stop moving when the UI is open for the quest, but I don't get where to put the code for the bool variable. And then put to write for the code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class QuestGiver : MonoBehaviour
{
public Quest quest;
public GameObject questWindow;
public Text titleText;
public Text descriptionText;
public Movement2 player;
public void OpenQuestWindow()
{
questWindow.SetActive(true);
titleText.text = quest.title;
descriptionText.text = quest.description;
}
public void AcceptQuest()
{
questWindow.SetActive(false);
quest.isActive = true;
player.quest = quest;
}
void Start()
{
questWindow = GameObject.Find("QuestWindow");
questWindow.SetActive(false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Movement2 : MonoBehaviour
{
[SerializeField] CharacterController controller;
private Vector3 playerVelocity;
private bool groundedPlayer;
public float playerSpeed = 5.0f;
private float jumpHeight = 40f;
public float gravityValue = -9.81f;
public Transform Cam;
public KeyCode jump;
public KeyCode FlyUp;
public KeyCode FlyDown;
public bool ablefly;
private float FlyMax = 250f;
public Quest quest;
public int SoulHP = 30;
private void Start()
{
// add character controller
// controller = gameObject.AddComponent<CharacterController>();
}
void Update()
{
// check if the player is grounded and the vector3.y < 0, if one of this condition is true set back the playerVelocity.y = 0f;
groundedPlayer = controller.isGrounded;
if (groundedPlayer && playerVelocity.y < 0)
{
playerVelocity.y = 0f;
}
Vector3 flyup = new Vector3(0, 50, 0);
Vector3 flydown = new Vector3(0, -50, 0);
Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Look for the camera y Rotation and multiply it for the vector 3 move in order make the player direction y as same as the camera.
Vector3 FollowCam = Quaternion.Euler(0, Cam.eulerAngles.y, 0) * move;
controller.Move(FollowCam * Time.deltaTime * playerSpeed);
// makes the player jump by adding a float value to the vector 3 y
if (Input.GetKeyDown(jump) && ablefly == false && groundedPlayer == true)
{
playerVelocity.y += 4f;
//print("Diocane");
}
if (Input.GetKeyDown(FlyUp) && ablefly == true)
{
//flyup = Vector3.ClampMagnitude(flyup, 250f);
gravityValue = 0;
controller.Move(flyup * Time.deltaTime * playerSpeed);
}
if (Input.GetKeyDown(FlyDown) && ablefly == true)
{
//flyup = Vector3.ClampMagnitude(flyup, 250f);
gravityValue = 0;
controller.Move(flydown * Time.deltaTime * playerSpeed);
}
playerVelocity.y += gravityValue * Time.deltaTime;
controller.Move(playerVelocity * Time.deltaTime);
}
public void GoFind()
{
if (quest.isActive)
{
quest.goal.ItemCollected();
if (quest.goal.IsReached())
SoulHP += 30;
}
}
}
You could set the Time.timeScale to 0 when the ui is opened to make anything using deltaTime stop moving.
In the OpenQuestWindow() you would add Time.timeScale = 0;
and in the AcceptQuest() method add Time.timeScale = 1; to set it back to normal
This looks like a Similar question. please check this and ask me if you don't get that.
EDIT:
This is not a tested code, but I hope will give you an idea, of how to proceed.
1- define a public bool variable mybool in Movement2 script
2- call that bool in the QuestGiver Script like this:
void Start() {
questWindow = GameObject.Find("QuestWindow");
questWindow.SetActive(false);
player.mybool = false; // or get the player gameobject by tag...
}
update your Movement2 update function to this:
void Update() {
if (mybool) {
// check if the player is grounded and the vector3.y < 0, if one of this condition is true set back the playerVelocity.y = 0f;
groundedPlayer = controller.isGrounded;
if (groundedPlayer && playerVelocity.y < 0) {
playerVelocity.y = 0 f;
}
Vector3 flyup = new Vector3(0, 50, 0);
Vector3 flydown = new Vector3(0, -50, 0);
Vector3 move = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Look for the camera y Rotation and multiply it for the vector 3 move in order make the player direction y as same as the camera.
Vector3 FollowCam = Quaternion.Euler(0, Cam.eulerAngles.y, 0) * move;
controller.Move(FollowCam * Time.deltaTime * playerSpeed);
// makes the player jump by adding a float value to the vector 3 y
if (Input.GetKeyDown(jump) && ablefly == false && groundedPlayer == true) {
playerVelocity.y += 4 f;
//print("Diocane");
}
if (Input.GetKeyDown(FlyUp) && ablefly == true) {
//flyup = Vector3.ClampMagnitude(flyup, 250f);
gravityValue = 0;
controller.Move(flyup * Time.deltaTime * playerSpeed);
}
if (Input.GetKeyDown(FlyDown) && ablefly == true) {
//flyup = Vector3.ClampMagnitude(flyup, 250f);
gravityValue = 0;
controller.Move(flydown * Time.deltaTime * playerSpeed);
}
playerVelocity.y += gravityValue * Time.deltaTime;
controller.Move(playerVelocity * Time.deltaTime);
}
if (!camMove) {
Vector3 move = new Vector3(0, 0, 0);
}
}
Now update that bool in QuestGiver:
public void OpenQuestWindow()
{
questWindow.SetActive(true);
titleText.text = quest.title;
descriptionText.text = quest.description;
player.mybool = false;
}
public void AcceptQuest()
{
questWindow.SetActive(false);
quest.isActive = true;
player.quest = quest;
player.mybool = true;
}
I have a character rotation, it is based on this. There is a joystick. If you put your finger first on the rotation of the character, and then on the joystick - everything is fine. But if at first on the joystick, and after trying to turn, nothing will happen! Help. A few days can not decide
void Start()
{
mContr = GameObject.FindGameObjectWithTag("Joystick").GetComponent<MobileController>();
}
void Update()
{
if(xp > 0)
{
deadStatus = false;
ch_animator.SetBool("dead", false);
}
if(xp <= 0)
{ if(deadStatus == false)
{
menu.gameObject.SetActive(false);
ch_animator.SetBool("Run", false);
ch_animator.SetBool("Walk", false);
ch_animator.SetBool("right", false);
ch_animator.SetBool("left", false);
ch_animator.SetBool("dead", true);
dead.gameObject.SetActive(true);
if(GameObject.Find("selectLvl"))
{
GameObject.Find("selectLvl").SetActive(false);
}
if(GameObject.Find("settings"))
{
GameObject.Find("settings").SetActive(false);
}
deadStatus = true;
}
}
if(eat > 10)
{
if(xp>0&&xp!=100) {
xp += Time.deltaTime * 1f;
eat -= Time.deltaTime * 0.5f;
}
speedMove = 3;
} else {
speedMove = 1;
if(eat < 1 ) {
xp -= Time.deltaTime * 0.06f;
}
}
if(xp.ToString() != hpText.text) {
hpText.text = Math.Round(xp).ToString();
}
if(eat.ToString() != eatText.text)
{
eatText.text = Math.Round(eat).ToString();
}
if(star.ToString() != starText.text)
{
starText.text = star.ToString();
}
if(gun.ToString() != gunText.text)
{
gunText.text = gun.ToString();
}
int k = 1;
while(Input.touchCount > k)
{
Vector2 deltaposition = Vector2.zero;
if(Input.GetTouch(k).position.x > Screen.width / 2 - 100)
{
if(k == 0)
deltaposition = Input.GetTouch(k).deltaPosition;
deltaposition.x /= Screen.dpi;
if(deltaposition.x != 0)
{
float speedX = 3 * deltaposition.x;
player.transform.Rotate(Vector3.up * speedX);
}
}
k++;
}
CharacterMove();
GameGravity();
}
Jostic code
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class MobileController: MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image jostickBG;
[SerializeField]
private Image jostick;
public Vector2 inputVector;
private void Start()
{
jostickBG = GetComponent<Image>();
jostick = transform.GetChild(0).GetComponent<Image>();
}
public virtual void OnPointerDown(PointerEventData ped)
{
}
public virtual void OnPointerUp(PointerEventData ped)
{
inputVector = Vector2.zero;
jostick.rectTransform.anchoredPosition = Vector2.zero;
}
public virtual void OnDrag(PointerEventData ped)
{
Vector2 pos;
if(RectTransformUtility.ScreenPointToLocalPointInRectangle(jostickBG.rectTransform, ped.position, ped.pressEventCamera, out pos))
{
pos.x = (pos.x / jostickBG.rectTransform.sizeDelta.x);
pos.y = (pos.y / jostickBG.rectTransform.sizeDelta.y);
inputVector = new Vector2(pos.x * 2 - 0, pos.y * 2 - 0);
inputVector = (inputVector.magnitude > 1.0f) ? inputVector.normalized : inputVector;
jostick.rectTransform.anchoredPosition = new Vector2(inputVector.x * (jostickBG.rectTransform.sizeDelta.x / 2), inputVector.y * (jostickBG.rectTransform.sizeDelta.y / 2));
}
}
public float Horizontal()
{
return inputVector.x;
}
public float Vertical()
{
return inputVector.y;
}
}
the problem is coming from the while loop:
your logic is only for k=0 on rotation player and k=1 for joy, and you begin to enter in the while loop with the value k=1, the test if is always false if for this value its the position of rotation player
if(Input.GetTouch(k).position.x > Screen.width / 2 - 100)
i suggest you to refactor your code like that:
when you have 2 touches k=0 and k=1, find the right action for each touches.
i dont understant why you dont enter in the while loop with k=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.