Instantiate Game Object floating in air - c#

I'm making a tower defense game in Unity 3D C#. The goal is for players to place objects in a grid (grid code below). I'm having trouble getting the "Corner" object to get placed on the ground, it's just appearing anywhere in the Y axis that I click. I'm confused because I'm using the same exact script for a different item and it's working fine (I just changed out the words "wall" for "corner"). The only difference between the items is that one is a straight line and one is an L shape (I did this through making two cubes, combining them, and putting them as a child Empty game object called "corner"). Any ideas?
using UnityEngine;
public class Grid : MonoBehaviour
{
[SerializeField]
private float size = 1f;
public Vector3 GetNearestPointOnGrid(Vector3 position)
{
position -= transform.position;
int xCount = Mathf.RoundToInt(position.x / size);
int yCount = Mathf.RoundToInt(position.y / size);
int zCount = Mathf.RoundToInt(position.z / size);
Vector3 result = new Vector3(
(float)xCount * size,
(float)yCount * size,
(float)zCount * size);
result += transform.position;
return result;
}
private void OnDrawGizmos()
{
Gizmos.color = Color.yellow;
for (float x = 0; x < 40; x += size)
{
for (float z = 0; z < 40; z += size)
{
var point = GetNearestPointOnGrid(new Vector3(x, 0f, z));
Gizmos.DrawSphere(point, 0.1f);
}
}
}
}
CornerPlacer:
using UnityEngine;
public class CornerPlacer : MonoBehaviour
{
private Grid grid;
public GameObject corner;
public bool placeCornerMode;
private void Awake()
{
grid = FindObjectOfType<Grid>();
}
void Update()
{
if (placeCornerMode)
{
GetItemsCorner();
}
}
void GetItemsCorner()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hitInfo))
{
PlaceCornerNear(hitInfo.point);
placeCornerMode = false;
}
}
}
private void PlaceCornerNear(Vector3 clickPoint)
{
var finalPosition = grid.GetNearestPointOnGrid(clickPoint);
Instantiate(corner, finalPosition, Quaternion.identity);
}
public void CornerModeOn()
{
placeCornerMode = true;
}
}

Related

Unity - how to make an object face the direction of its movement?

I'm trying to make a character prefab face the direction in which its moving. I've tired all sorts of things, with and without rigidbodies but nothing seems to work. The thing is, it does actually face in the correct direction. But once its there, it starts to rotate the whole prefab and it goes down into the ground.
The character holds a 3D shield, and goes towards a tower. So once it reaches the tower it raises the shield which in turn rotates the whole character down into the ground. I would like it to just rotate in the X and Z axis and never change the Y axis.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlockRadar : MonoBehaviour
{
// Start is called before the first frame update
public Transform Tower;
private GameObject[] multipleBlocks;
public Transform closestBlock;
public bool blockContact;
public float currentDistance;
public float stopDistance;
public Animator animator;
public int damage;
public float attackspeed;
private float canAttack;
public float moveSpeed = 5f;
public Vector3 distance;
private Vector3 movement;
void Start()
{
closestBlock = null;
blockContact = false;
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.position;
pos.y = 0;
pos.x = 0;
transform.position = pos;
closestBlock = getClosestBlock();
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(1, 0.7f, 0, 1);
Vector3 direction = closestBlock.position - transform.position;
direction.Normalize();
movement = direction;
float dist = Vector3.Distance(closestBlock.position, transform.position);
if (dist <= 1.5f)
{
{
blockContact = true;
animator.SetBool("Moving", false);
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
if (dist > 1.5f)
{
transform.forward = movement;
blockContact = false;
Debug.Log("Lost contact with block");
animator.SetBool("Moving", true);
moveCharacter(movement);
}
}
public void Attack()
{
Debug.Log("ATTACKING!");
Damage(closestBlock.transform);
animator.SetTrigger("Attacking");
}
private void FixedUpdate()
{
}
void moveCharacter(Vector3 direction)
{
transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World);
}
void DistanceToTower()
{
if (Tower)
{
float dist = Vector3.Distance(Tower.position, transform.position);
if (dist <= 1)
{
{
blockContact = true;
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
}
}
//when the object carrying this script is destroyed
private void OnDestroy()
{
if (closestBlock !=null)
{
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(0, 0, 0, 0);
}
}
public Transform getClosestBlock()
{
multipleBlocks = GameObject.FindGameObjectsWithTag("Block");
float closestDistance = Mathf.Infinity;
Transform trans = null;
//finds all blocks in the scene
foreach (GameObject go in multipleBlocks)
{
currentDistance = Vector3.Distance(transform.position, go.transform.position);
if (currentDistance < closestDistance)
{
closestDistance = currentDistance;
trans = go.transform;
}
}
return trans;
}
void Damage(Transform block)
{
Tower_Stats e = block.GetComponent<Tower_Stats>();
if (e != null)
{
e.TakeDamage(damage);
}
}
}
I would be really, really grateful for any help. As I said before I used to have rigidbodies on the character, but I removed them since I thought maybe they were the fault. But doesnt seem like it. One other thing I've noticed is that when the prefab is instantiated, its children doesnt have the correct position values. Not sure why. But if that could be a clue I just thought I'd let you know.

Can't raycast mouse click at good position on my map

I decided to develop a game of gomoku, which the principle is very simple : you have to place circle or cross in a map of tile and the goal is to align 5 items. For that i choose to use unity in 2d and i use prefab with tile image to create my map. The problem i encountered is that, in my script GameManager, when i raycast my mouse click position, the click position is not the same as my tile of my board which i generated so i can't place item on my map.
BoardGenerator.cs
public float startX;
public float startY;
public Transform tiles;
public int BOARD_SIZE = 19;
void Start()
{
CreateBoard();
}
void Update()
{
}
void CreateBoard()
{
float tileX = tiles.GetComponent<SpriteRenderer>().bounds.size.x;
float tileY = tiles.GetComponent<SpriteRenderer>().bounds.size.y;
float yCor = Camera.main.ScreenToWorldPoint(new Vector3(0, startY, 0f)).y;
int count = 0;
for (int i = 0; i < BOARD_SIZE; i++)
{
float xCor = Camera.main.ScreenToWorldPoint(new Vector3(startX, 0, 0f)).x;
for (int j = 0; j < BOARD_SIZE; j++)
{
Vector3 tilesPos = new Vector3(xCor, yCor, 0f);
Transform tile = Instantiate(tiles, tilesPos, Quaternion.identity);
tile.gameObject.name = "Tile" + count;
count++;
xCor += tileX;
}
yCor -= tileY;
}
}
GameManager.cs
public int firstPlayer;
private PlayerTurn playerTurn;
public Sprite xSprite;
public Sprite oSprite;
public enum PlayerTurn
{
PLAYER1,
PLAYER2,
BOT
}
void Start()
{
if (firstPlayer == 1)
playerTurn = PlayerTurn.PLAYER1;
else
playerTurn = PlayerTurn.PLAYER2;
}
void Update()
{
Play();
}
void Play()
{
if (Input.GetMouseButtonDown(0))
{
float xCor = Camera.main.ScreenToWorldPoint(Input.mousePosition).x;
float yCor = Camera.main.ScreenToWorldPoint(Input.mousePosition).y;
Vector2 origin = new Vector2(xCor, yCor);
RaycastHit2D hit = Physics2D.Raycast(origin, Vector2.zero, 0);
Debug.Log("mouse = " + origin);
Debug.Log("first tile = " + (Vector2)GameObject.Find("Tile0").transform.position);
if (hit.collider != null && hit.transform.gameObject.tag.Equals("Tile"))
{
Debug.Log("hit");
if (this.playerTurn == PlayerTurn.PLAYER1)
{
hit.transform.gameObject.GetComponent<SpriteRenderer>().sprite = xSprite;
hit.transform.tag = "crossTile";
playerTurn = PlayerTurn.PLAYER2;
}
else if (this.playerTurn == PlayerTurn.PLAYER2)
{
hit.transform.gameObject.GetComponent<SpriteRenderer>().sprite = oSprite;
hit.transform.tag = "roundTile";
playerTurn = PlayerTurn.PLAYER1;
}
}
}
}
You can take advantage of a different overload for the Raycast in order to return the object hit, like so:
Ray ray = camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics2D.Raycast(ray, out hit, rayDist)){
GameObject obj = hit.transform.gameObject;
// Do something with obj
}
In the above example, 'obj' will be the GameObject that was hit by the Raycast, so you can get its position by then referencing:
obj.transform.position
The reason for:
GameObject obj = hit.transform.gameObject;
is that 'hit.gameObject' will return a parent object with a Rigidbody, if there is one. This can lead to a lot of confusion down the track when working with more complex object structures, so it is good practise to grab 'hit.transform.gameObject', which will always return the GameObject that the collider the Raycast hit is attached to.

Create shapes in android with unity

I am trying to create custom shapes in unity with free hand but i am getting very thick shapes Like this
I am using this code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class DrawLine : MonoBehaviour
{
public Text toshow;
private LineRenderer line;
private bool isMousePressed;
public List<Vector3> pointsList;
private Vector3 mousePos;
// Structure for line points
struct myLine
{
public Vector3 StartPoint;
public Vector3 EndPoint;
};
// -----------------------------------
void Awake ()
{
// Create line renderer component and set its property
line = gameObject.AddComponent<LineRenderer> ();
line.material = new Material (Shader.Find ("Particles/Additive"));
line.SetVertexCount (0);
line.SetWidth (0.1f, 0.1f);
line.SetColors (Color.green, Color.green);
line.useWorldSpace = true;
isMousePressed = false;
pointsList = new List<Vector3> ();
// renderer.material.SetTextureOffset(
}
// Following method adds collider to created line
private void addColliderToLine(Vector3 startPos, Vector3 endPos)
{
BoxCollider col = new GameObject("Collider").AddComponent<BoxCollider> ();
col.gameObject.tag = "Box";
col.transform.parent = line.transform; // Collider is added as child object of line
float lineLength = Vector3.Distance (startPos, endPos); // length of line
col.size = new Vector3 (lineLength, 0.1f, 1f); // size of collider is set where X is length of line, Y is width of line, Z will be set as per requirement
Vector3 midPoint = (startPos + endPos)/2;
col.transform.position = midPoint; // setting position of collider object
// Following lines calculate the angle between startPos and endPos
float angle = (Mathf.Abs (startPos.y - endPos.y) / Mathf.Abs (startPos.x - endPos.x));
if((startPos.y<endPos.y && startPos.x>endPos.x) || (endPos.y<startPos.y && endPos.x>startPos.x))
{
angle*=-1;
}
angle = Mathf.Rad2Deg * Mathf.Atan (angle);
col.transform.Rotate (0, 0, angle);
}
void createCollider(){
for (int i=0; i < pointsList.Count - 1; i++) {
addColliderToLine(pointsList[i],pointsList[i+1]);
}
}
public void MouseDrag(){
#if UNITY_ANDROID
Vector2 touchPos = Input.touches[0].position;
mousePos = Camera.main.ScreenToWorldPoint (new Vector3(touchPos.x, touchPos.y, 0));
mousePos.z = 0;
if (!pointsList.Contains (mousePos)) {
pointsList.Add (mousePos);
line.SetVertexCount (pointsList.Count);
line.SetPosition (pointsList.Count - 1, (Vector3)pointsList [pointsList.Count - 1]);
}
#endif
}
public void MouseBeginDrag(){
isMousePressed = true;
line.SetVertexCount (0);
GameObject[] ob = GameObject.FindGameObjectsWithTag("Box");
for(int i=0; i<ob.Length; i++){
Destroy(ob[i]);
}
pointsList.RemoveRange (0, pointsList.Count);
line.SetColors (Color.green, Color.green);
}
public void MouseFinishDrag(){
createCollider();
isMousePressed = false;
}
// -----------------------------------
void Update ()
{
}
}
Please Help me to decrease the width of a line. I am not able to decrease the width of a line. Please help me

Set appropriate Collider type for each Mesh that gets created

I'm generating a bunch of basic sphere and capsule mesh objects at runtime in a 3D fractal-based piece I'm working on, and I'm stuck on how to apply the appropriate Collider type when each object gets created.
My script randomly selects from either spheres or capsules and arranges them based on a range of orientation possibilities. I need help on how to assign the appropriate Collider when each mesh gets created.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class BuildFractal : MonoBehaviour
{
public Mesh[] meshes;
public Material material;
public Material[,] materials;
private Rigidbody rb;
public int maxDepth; // max children depth
private int depth;
public float childScale; // set scale of child objects
public float spawnProbability; // determine whether a branch is created or not
public float maxRotationSpeed; // set maximium rotation speed
private float rotationSpeed;
public float maxTwist;
public Text positionText;
public int objectMass;
// Create arrays for direction and orientation data
private static Vector3[] childDirections = {
Vector3.up,
Vector3.right,
Vector3.left,
Vector3.forward,
Vector3.back,
// Vector3.down
};
private static Quaternion[] childOrientations = {
Quaternion.identity,
Quaternion.Euler(0f, 0f, -90f),
Quaternion.Euler(0f, 0f, 90f),
Quaternion.Euler(90f, 0f, 0f),
Quaternion.Euler(-90f, 0f, 0f),
// Quaternion.Euler(180f, 0f, 0f)
};
private void Start ()
{
Rigidbody rb;
rotationSpeed = Random.Range(-maxRotationSpeed, maxRotationSpeed);
transform.Rotate(Random.Range(-maxTwist, maxTwist), 0f, 0f);
if (materials == null)
{
InitializeMaterials();
}
// Select from random range of meshes
gameObject.AddComponent<MeshFilter>().mesh = meshes[Random.Range(0, meshes.Length)];
// Select from random range of colors
gameObject.AddComponent<MeshRenderer>().material = materials[depth, Random.Range(0, 2)];
// Add Rigigbody to each object
rb = gameObject.AddComponent<Rigidbody>();
rb.useGravity = false;
rb.mass = objectMass;
// Create Fractal Children
if (depth < maxDepth)
{
StartCoroutine(CreateChildren());
}
}
private void Update ()
{
transform.Rotate(0f, rotationSpeed * Time.deltaTime, 0f);
}
private void Initialize (BuildFractal parent, int childIndex)
{
maxRotationSpeed = parent.maxRotationSpeed;
// copy mesh and material references from parent object
meshes = parent.meshes;
materials = parent.materials;
maxTwist = parent.maxTwist;
// set depth and scale based on variables defined in parent
maxDepth = parent.maxDepth;
depth = parent.depth + 1;
childScale = parent.childScale;
transform.parent = parent.transform; // set child transform to parent
// transform.localScale = Vector3.one * childScale;
transform.localScale = Vector3.one * Random.Range(childScale / 10, childScale * 1);
transform.localPosition = childDirections[childIndex] * (Random.Range((0.1f + 0.1f * childScale),(0.9f + 0.9f * childScale)));
transform.localRotation = childOrientations[childIndex];
spawnProbability = parent.spawnProbability;
}
private void InitializeMaterials ()
{
materials = new Material[maxDepth + 1, 2];
for (int i = 0; i <= maxDepth; i++)
{
float t = i / (maxDepth - 1f);
t *= t;
// Create a 2D array to hold color progressions
materials[i, 0] = new Material(material);
materials[i, 0].color = Color.Lerp(Color.gray, Color.white, t);
materials[i, 1] = new Material(material);
materials[i, 1].color = Color.Lerp(Color.white, Color.cyan, t);
}
// materials[maxDepth, 0].color = Color.white;
materials[maxDepth, 1].color = Color.white;
}
private IEnumerator CreateChildren ()
{
for (int i = 0; i < childDirections.Length; i++)
{
if (Random.value < spawnProbability)
{
yield return new WaitForSeconds(Random.Range(0.1f, 1.5f));
new GameObject("Fractal Child").AddComponent<BuildFractal>().Initialize(this, i);
}
}
}
/*void OnCollisionEnter(Collision col)
{
if(col.gameObject.name == "Fractal Child")
{
Destroy(this.gameObject);
}
}*/
}
Add a mesh collider after you instantiate and set the same mesh, job done!
http://docs.unity3d.com/ScriptReference/MeshCollider.html
that should sort it out programmatically

wander & chase AI code not working

Put together an enemy AI system for an enemy in which it should wander around idly when the player is not within distance to the enemy and when player is within enemy distance it should initiate chase behaviour and chase after player until player has managed to exit out of the enemy's chase radius.
Currently the enemy is able to wonder freely yet when the player comes within proximity of the enemy the enemy will carry on wandering instead of chasing player.
Anyone help me fix this problem?
Code is as follows.
public enum AIState
{
Chasing,
Wander
}
private float maxSpeed;
private float maxRotation;
private float chaseDistance;
private float hysteresis;
private Texture2D texture;
private Vector2 drawingOrigin;
private Vector2 position;
public AIState aiState = AIState.Wander;
private float orientation;
private Random random = new Random();
private Rectangle viewportbounds;
public Rectangle boundingBox;
public Vector2 playerPosition;
private Vector2 heading;
public Virtual_Aliens(Rectangle pos, Rectangle b)
{
position = new Vector2(300, 400);
boundingBox = new Rectangle(pos.X, pos.Y, pos.Width, pos.Height);
viewportbounds = new Rectangle(b.X, b.Y, b.Width, b.Height);
orientation = 0.0f;
heading = new Vector2(0, 0);
maxSpeed = 2.0f;
maxRotation = 0.20f;
hysteresis = 15.0f;
chaseDistance = 250.0f;
Thread.Sleep(200);
random = new Random();
}
public void LoadContent(ContentManager Content)
{
texture = Content.Load<Texture2D>("images/asteroid");
}
private Vector2 OrientationAsVector(float orien)
{
Vector2 orienAsVect;
orienAsVect.X = (float)Math.Cos(orien);
orienAsVect.Y = (float)Math.Sin(orien);
return orienAsVect;
}
Vector2 wanderPosition = new Vector2();
public void Wander()
{
// the max +/- the agent will wander from its current position
float wanderLimits = 0.5f;
// this defines what proportion of its maxRotation speed the agent will turn
float turnFactor = 0.15f;
// randomly define a new position
wanderPosition.X += MathHelper.Lerp(-wanderLimits, wanderLimits, (float)random.NextDouble());
wanderPosition.Y += MathHelper.Lerp(-wanderLimits, wanderLimits, (float)random.NextDouble());
if (wanderPosition != Vector2.Zero)
{
wanderPosition.Normalize();
}
orientation = TurnToFace(wanderPosition, orientation, turnFactor * maxRotation);
heading = OrientationAsVector(orientation);
position += heading * 0.5f * maxSpeed;
WrapForViewport();
}
private void WrapForViewport()
{
if (position.X < 0)
{
position.X = viewportbounds.Width;
}
else if (position.X > viewportbounds.Width)
{
position.X = 0;
}
if (position.Y < 0)
{
position.Y = viewportbounds.Height;
}
else if (position.Y > viewportbounds.Height)
{
position.Y = 0;
}
}
private float WrapAngle(float radian)
{
while (radian < -MathHelper.Pi)
{
radian += MathHelper.TwoPi;
}
while (radian > MathHelper.Pi)
{
radian -= MathHelper.TwoPi;
}
return radian;
}
private float TurnToFace(Vector2 steering, float currentOrientation, float turnSpeed)
{
float newOrientation;
float desiredOrientation;
float orientationDifference;
float x = steering.X;
float y = steering.Y;
// the desiredOrientation is given by the steering vector
desiredOrientation = (float)Math.Atan2(y, x);
// find the difference between the orientation we need to be
// and our current Orientation
orientationDifference = desiredOrientation - currentOrientation;
// now using WrapAngle to get result from -Pi to Pi
// ( -180 degrees to 180 degrees )
orientationDifference = WrapAngle(orientationDifference);
// clamp that between -turnSpeed and turnSpeed.
orientationDifference = MathHelper.Clamp(orientationDifference, -turnSpeed, turnSpeed);
// the closest we can get to our target is currentAngle + orientationDifference.
// return that, using WrapAngle again.
newOrientation = WrapAngle(currentOrientation + orientationDifference);
return newOrientation;
}
public void Update(GameTime gameTime)
{
if (aiState == AIState.Wander)
{
chaseDistance -= hysteresis / 2;
}
else if (aiState == AIState.Chasing)
{
chaseDistance += hysteresis / 2;
}
float distanceFromPlayer = Vector2.Distance(position, playerPosition);
if (distanceFromPlayer > chaseDistance)
{
aiState = AIState.Wander;
}
else
{
aiState = AIState.Chasing;
}
float currentSpeed;
if (aiState == AIState.Chasing)
{
orientation = TurnToFace(playerPosition, orientation, maxRotation);
currentSpeed = maxSpeed;
}
else if (aiState == AIState.Wander)
{
Wander();
}
}
public void Draw(SpriteBatch spriteBatch)
{
boundingBox.X = (int)position.X;
boundingBox.Y = (int)position.Y;
drawingOrigin = new Vector2(texture.Width / 2, texture.Height / 2);
spriteBatch.Draw(texture, boundingBox, null, Color.White, orientation, drawingOrigin, SpriteEffects.None, 0.0f);
}
public Vector2 PlayerPosition
{
set
{
playerPosition = value;
}
get
{
return playerPosition;
}
}
You get the distance from the player using:
float distanceFromPlayer = Vector2.Distance(position, playerPosition);
But you never actually set the variable playerPosition in your class. So effectively if the enemy is within the chase radius from the point (0,0) they will chase your player, but otherwise will they will just wander around.
I would recommend doing one of two things to solve this issue.
First off you could change the parameters of your Update method to take in the Vector2 of the players position.
A second approach (the one I would personally choose) would be to add a new field (class variable) that is of type Player and then in your Virtual_Aliens' constructor pass in an instance of the player class. That way any time you reference playerPosition you would be able to just say player.Position (or however you have your position field named).

Categories