How can i make the Invisible Walls script to work and take effect? - c#

The idea is to make that when the player is walking to the edge of the terrain he will stop wont be able to continue and fall.
And in my case i want the objects that move forward when they collide with the invisible wall the object will turn lerp back and move to the other side of the invisible walls.
Another problem that might come up later i read about is that if the objects moving too fast to the invisible walls there is a bug that let them move through ? Not sure about it.
This is a screenshot showing the invisible walls. I created a box collider set the Is Trigger to be on and set the 500 600 500 same as the terrain size.
This is the script of the Invisible Walls: The script i attached it to the Terrain:
using UnityEngine;
using System.Collections;
public class InvisibleWalls : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerExit(Collider other)
{
}
}
This is the script that create the space ships clone of them and make them move forward. But when they get to the edge of the terrain they just gone out. And i want them to lkerp/turn back to the other side.
This script is attached to the Spheres GameObject:
using System;
using UnityEngine;
using Random = UnityEngine.Random;
using System.Collections;
using System.Collections.Generic;
public class SphereBuilder : MonoBehaviour
{
public GameObject SpaceShip;
GameObject[] spheres;
public float moveSpeed = 50;
// for tracking properties change
private Vector3 _extents;
private int _sphereCount;
private float _sphereSize;
/// <summary>
/// How far to place spheres randomly.
/// </summary>
public Vector3 Extents;
/// <summary>
/// How many spheres wanted.
/// </summary>
public int SphereCount;
public float SphereSize;
private void Start()
{
spheres = GameObject.FindGameObjectsWithTag("MySphere");
}
private void OnValidate()
{
// prevent wrong values to be entered
Extents = new Vector3(Mathf.Max(0.0f, Extents.x), Mathf.Max(0.0f, Extents.y), Mathf.Max(0.0f, Extents.z));
SphereCount = Mathf.Max(0, SphereCount);
SphereSize = Mathf.Max(0.0f, SphereSize);
}
private void Reset()
{
Extents = new Vector3(250.0f, 20.0f, 250.0f);
SphereCount = 100;
SphereSize = 20.0f;
}
private void Update()
{
UpdateSpheres();
MoveShips ();
}
private void MoveShips()
{
foreach (Transform child in spheres[0].transform)
{
child.transform.position += Vector3.forward * Time.deltaTime * moveSpeed;
}
}
private void UpdateSpheres()
{
if (Extents == _extents && SphereCount == _sphereCount && Mathf.Approximately(SphereSize, _sphereSize))
return;
// cleanup
var spheres = GameObject.FindGameObjectsWithTag("Sphere");
foreach (var t in spheres)
{
if (Application.isEditor)
{
DestroyImmediate(t);
}
else
{
Destroy(t);
}
}
var withTag = GameObject.FindWithTag("Terrain");
if (withTag == null)
throw new InvalidOperationException("Terrain not found");
for (var i = 0; i < SphereCount; i++)
{
var o = Instantiate(SpaceShip);
o.tag = "Sphere";
o.transform.SetParent(gameObject.transform);
o.transform.localScale = new Vector3(SphereSize, SphereSize, SphereSize);
// get random position
var x = Random.Range(-Extents.x, Extents.x);
var y = Extents.y; // sphere altitude relative to terrain below
var z = Random.Range(-Extents.z, Extents.z);
// now send a ray down terrain to adjust Y according terrain below
var height = 10000.0f; // should be higher than highest terrain altitude
var origin = new Vector3(x, height, z);
var ray = new Ray(origin, Vector3.down);
RaycastHit hit;
var maxDistance = 20000.0f;
var nameToLayer = LayerMask.NameToLayer("Terrain");
var layerMask = 1 << nameToLayer;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
var distance = hit.distance;
y = height - distance + y; // adjust
}
else
{
Debug.LogWarning("Terrain not hit, using default height !");
}
// place !
o.transform.position = new Vector3(x, y, z);
}
_extents = Extents;
_sphereCount = SphereCount;
_sphereSize = SphereSize;
}
}
And this is a small short video clip showing what happen to the space ships when they get to the terrain edge:
Spaceships video clip
Update what i did so far:
In top of script added:
public Terrain terrain;
private Vector3 boundLower;
private Vector3 boundUpper;
In Start function i added:
private void Start()
{
spheres = GameObject.FindGameObjectsWithTag("MySphere");
var withTag = GameObject.FindWithTag("Terrain");
if (withTag == null)
throw new InvalidOperationException("Terrain not found");
boundLower = terrain.transform.position - terrain.transform.size / 2;
boundUpper = terrain.transform.position + terrain.transform.size / 2;
}
But getting errors on both lines: size property not exist:
boundLower = terrain.transform.position - terrain.transform.size / 2;
boundUpper = terrain.transform.position + terrain.transform.size / 2;
And changed the MoveShips function to this:
private Vector3 direction = Vector3.forward;
private void MoveShips() {
foreach (var child in spheres) {
var pos = child.transform.position + direction * Time.deltaTime * moveSpeed;
pos.x = Mathf.Clamp(pos.x, boundLower.x, boundUpper.x);
pos.z = Mathf.Clamp(pos.z, boundLower.z, boundUpper.z);
if (pos.x == boundLower.x || pos.x == boundUpper.x) direction.x = - direction.x;
if (pos.z == boundLower.z || pos.z == boundUpper.z) direction.z = - direction.z;
child.transform.position = pos;
}
}

I would suggest modifying MoveShips() changing Vector3.forward to a variable and flipping it when bounds are reached:
private Vector3 direction = Vector3.forward;
private void MoveShips() {
foreach (var child in spheres) {
var pos = child.transform.position + direction * Time.deltaTime * moveSpeed;
pos.x = Mathf.Clamp(pos.x, boundLower.x, boundUpper.x);
pos.z = Mathf.Clamp(pos.z, boundLower.z, boundUpper.z);
if (pos.x == boundLower.x || pos.x == boundUpper.x) direction.x = - direction.x;
if (pos.z == boundLower.z || pos.z == boundUpper.z) direction.z = - direction.z;
child.transform.position = pos;
}
}
This will remove unnecessary dependence on object collision engine for such a simple thing. Note, how this is making all ships to change direction when furthest reaches the bound. If you want them to move separately, you will need to move this logic to a separate script and attach it to a ship prefab.
And the bounds (boundLower and boundUpper) can be set either as script variables in editor or calculated from terrain, like:
boundLower = terrain.transform.position - terrain.TerrainData.size / 2;
boundUpper = terrain.transform.position + terrain.TerrainData.size / 2;
I would also suggest to move this:
var withTag = GameObject.FindWithTag("Terrain");
if (withTag == null)
throw new InvalidOperationException("Terrain not found");
out of Update() into Start() unless you do something really funky with it in the process.

Lets start working though your problems one by one :
Question : The objects do not collide, why?
Answer : Objects do not collide from with-in the collider, only from the outside.
What you need in your case is 4 box collider, one at each edge of the map
Question : Another problem that might come up later i read about is that if the objects moving too fast to the invisible walls there is a bug that let them move through ? Not sure about it.
This is only a problem with objects moving at bullet-like speeds, you could edit the rigidbody to have detection mode : "continuous" or continuous-dynamic which will avoid this issue
The scripts, i do not think you would need them in this case, your original idea was good, just the implementation with a single collider over the whole terrain, instead of 4 seperate "wall" colliders, was the issue. As for the rest of the logic, i did not try decipher through that, thus i can not comment on it.

Related

How to interact with objects with ARFoundation?

So the idea is to have a plane and grid placement system in augmented reality with the ability to place and move characters on grids. I already have an example for a mobile device, I have a script that generates grid and a script that allows me to place objects and it works just fine, however, I can't figure out how to use all of the above and if it's possible in AR. For example, I want to detect a plane then instantiate a level and put some objects on it.
Here's the script that is attached to the GridManager and is used to make a grid:
[SerializeField] private float size = 0.05f;
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.01f);
}
}
}
and here's the one that's attached to the PlacerManager and used to place objects on the grid:
private Grid grid;
private void Awake()
{
grid = FindObjectOfType<Grid>();
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hitInfo;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hitInfo))
{
PlaceCubeNear(hitInfo.point);
}
}
}
private void PlaceCubeNear(Vector3 clickPoint)
{
var finalPosition = grid.GetNearestPointOnGrid(clickPoint);
GameObject.CreatePrimitive(PrimitiveType.Cube).transform.position = finalPosition;
}
You can use the raycast options to identify different objects
Raycasting and/or colliders are the way to go.
The sample scene in AR Foundation has a script called PlaceOnPlane.cs that shows how you can detect when a user touches the screen. E.g:
if (Input.touchCount == 1) {
if (m_RaycastManager.Raycast(Input.GetTouch(0).position, s_Hits, TrackableType.PlaneWithinPolygon))
{
// Raycast hits are sorted by distance, so the first one
// will be the closest hit.
var hitPose = s_Hits[0].pose;
if (spawnedObject == null)
{
spawnedObject = Instantiate(m_PlacedPrefab, hitPose.position, hitPose.rotation);
}
}
}
This allows you to get the screen touch position and then raycast from it to the real world scene. In this example, a game object is instantiated in that location. For your case, you can instantiated a level if your hit a plane or a plane exist around the hit position.

Unity2D: Enemy doesn't follow Player when in its radius

Hey Guys I've been having a problem lately that I cant seem to solve.
A sprite is supposed to roam around (as it does) while nothing is inside its radius, however if the player moves close to it the sprite should theoretically move towards it and stop roaming.
The sprite doesn't follow the player and cant even see its tag since I cant even see the contents of the "Collider2D[] hits".
using System.Collections.Generic;
using UnityEngine;
public class FireCultist : MonoBehaviour
{
public float moveTimeSeconds; //Time it will take object to move, in seconds.
private float xMax = 10.0f; // The boundaries of the spawn area
private float yMax = 10.0f;
private float xMin = -10.0f; // The boundaries of the spawn area
private float yMin = -10.0f;
public int xDistanceRange; // The max distance you can move at one time
public int yDistanceRange;
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
public Vector2 start;
public Vector2 end;
public bool roam = true;
public Collider2D[] hits;
void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
rb2D = GetComponent<Rigidbody2D>();
inverseMoveTime = 1f / moveTimeSeconds;
InvokeRepeating("RandomMove", 0.0f, 5.0f);
}
void Update()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 10); // returns all colliders within radius of enemy
int i = 0;
while(hits.Length > i)
{
Debug.Log("Sees");
Debug.Log(hits[i]);
i++;
}
followPlayer();
if (roam)
{
Debug.Log("Roam");
transform.position = Vector2.MoveTowards(transform.position, end, inverseMoveTime * Time.deltaTime); //Random move to new position
}
}
public void followPlayer()
{
foreach (Collider2D hit in hits)
{
if (hit.tag == "Player") // if the player is within a radius
{
Debug.Log("Chasing Player");
transform.position = Vector2.MoveTowards(transform.position, GameObject.Find("Prefabs/Player").GetComponent<Transform>().position, inverseMoveTime * Time.deltaTime); // chase player
roam = false;
}
else
{
Debug.Log("Continues");
roam = true; // Continue RandomMove()
}
}
}
public void RandomMove() // gets random coordinates near enemy and moves there
{
float xDistance = Random.Range(-xDistanceRange, xDistanceRange); // check
float yDistance = Random.Range(-yDistanceRange, yDistanceRange);// check
if (xDistance < xMax && xDistance > xMin && yDistance < yMax && yDistance > yMin && roam == true) // If within boundaries of walking space
{
start = transform.position;
end = start + new Vector2(xDistance, yDistance);
Debug.Log("Roaming From : " + start + " To : " + end);
}
}
}
The roaming algorithm works however not too sure about the tag detection.
The script belongs to this enemy object
Player Object Properties
It looks like you are declaring a new hits variable during every update instead of using your class-level variable. This means the variable inside of followPlayer() will never be instantiated, and the information will not be passed between the two methods.
Try this:
void Update()
{
hits = Physics2D.OverlapCircleAll(transform.position, 10);
//...
}
You might also consider attaching a trigger collider to the enemy which sends a notification when the player enters/exits the trigger instead of relying on OverlapCircleAll()
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = false;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = true;
}
}

How can I rotate the camera around group of objects and not only a single one?

The camera is child of one soldier only and also the target is this soldier.
And the script attached to the camera.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public Transform target;
public float speed = 0.1f;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
transform.RotateAround(target.transform.position, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
}
But now I want to do two things. To make the camera to rotate around the whole soldiers and not only the specific one. And also to make the camera stop slowly when it's facing the soldiers. Now the camera is behind when starting the game.
Using a bool flag if true to make the camera rotate around the soldiers until it's facing them then stop rotation and keep moving with the soldiers.
If unchecked false make the camera rotating around the solders none stop.
UPDATE what I tried so far:
This code will make it rotating around all the soldiers and it's working fine:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
// Update is called once per frame
void Update()
{
RotateAround();
}
private void RotateAround()
{
transform.RotateAround(GetAverageLocationOfSoliders(), new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
total += soldier.transform.position;
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
}
Then I tried to add a slowDown bool flag variable, But I messed it all and it's not working at all.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
public float speed = 0.1f;
public bool slowDown = false;
private List<Vector3> SoldiersPositions = new List<Vector3>();
private List<Vector3> SoldiersFacingDirection = new List<Vector3>();
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void FixedUpdate()
{
RotateAround();
}
private void RotateAround()
{
var getSoldiers = GameObject.FindGameObjectsWithTag("Soldier");
foreach (GameObject soldier in getSoldiers)
{
SoldiersPositions.Add(soldier.transform.position);
SoldiersFacingDirection.Add(soldier.transform.forward);
}
var Center = GetAverageLocationOfSoliders();
var FacingDirections = GetAverageFacingDirectionOfSoldiers();
if (slowDown == true)
{
var D = transform.position - Center;
var CamAngle = Vector3.Angle(D, FacingDirections);
speed = speed - CamAngle;
}
transform.RotateAround(Center, new Vector3(0, 1, 0), 100 * Time.deltaTime * speed);
SoldiersPositions = new List<Vector3>();
SoldiersFacingDirection = new List<Vector3>();
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in SoldiersPositions)
total += soldier;
return total / SoldiersPositions.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageFacingDirectionOfSoldiers()
{
var total = new Vector3();
foreach (var soldierfacingdir in SoldiersFacingDirection)
total += soldierfacingdir;
return total / SoldiersFacingDirection.Count();
}
}
I'm not sure if the first code example only for the rotation is fine the way I wrote it. It's working but not sure if this is a good way to do the code ?
It seems to me that in the first code only the rotation the camera is a bit shaking or stuttering I mean the camera is not moving smooth when rotating around. It's almost hard to see in the game view in the editor but you can see it a bit in the scene view I think.
The reason I'm calling RotateAround(); in the Update is that the soldiers are in a move they are walking forward non stop.
How should I do the slowDown part ?
UPDATE 2:
This is the full coed now:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class CameraMove : MonoBehaviour
{
[Header("Spin")]
public bool spin = false;
public Vector3 Direction;
[Range(0, 300)]
public float speed = 10f;
public bool randomSpeed = false;
public bool randomDirection = false;
[Range(0f, 100f)]
public float timeDirChange;
public Vector3 defaultDirection;
[Space(5)]
[Header("Move in circles")]
public bool moveInCircles = true;
public GameObject rotateAroundTarget;
public Vector3 axis;//by which axis it will rotate. x,y or z.
public float rotationSpeed; //or the speed of rotation.
public float upperLimit, lowerLimit, delay;// upperLimit & lowerLimit: heighest & lowest height;
public bool randomHeight = false;
public bool stopRotatingWhenFacing = false;
private float height, prevHeight, time;//height:height it is trying to reach(randomly generated); prevHeight:stores last value of height;delay in radomness;
[Space(5)]
[Header("Follow objects")]
public GameObject[] objectsToFollow;
public bool randomFollow;
private float nextRotationTime = 0f;
private int counter = 0;
private List<GameObject> Soldiers = new List<GameObject>();
// Use this for initialization
void Start()
{
Soldiers.AddRange(GameObject.FindGameObjectsWithTag("Soldier"));
}
private void Update()
{
if (randomSpeed)
{
speed = UnityEngine.Random.Range(0, 300);
}
if (spin)
{
if (randomDirection == false)
{
nextRotationTime = 0;
timeDirChange = 0;
Direction = defaultDirection;
}
else
{
if (Time.time > nextRotationTime)
{
nextRotationTime += timeDirChange;
RandomDirection();
}
}
transform.Rotate(Direction, speed * Time.deltaTime);
}
else
{
timeDirChange = 0;
randomDirection = false;
randomSpeed = false;
}
if (moveInCircles)
{
MoveInCircles();
}
}
private void RandomDirection()
{
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
while (Direction == new Vector3(0, 0, 0))
{
counter++;
Direction = new Vector3(UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1), UnityEngine.Random.Range(-1, 1));
if (counter == 2)
{
Direction = new Vector3(1, 0, 0);
break;
}
}
counter = 0;
}
private void MoveInCircles()
{
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
time += Time.deltaTime;
//Sets value of 'height' randomly within 'upperLimit' & 'lowerLimit' after delay
if (time > delay)
{
prevHeight = height;
if (randomHeight)
{
height = UnityEngine.Random.Range(lowerLimit, upperLimit);
}
time = 0;
}
if (randomHeight == false)
{
height = transform.position.y;
}
if (randomHeight)
{
//Mathf.Lerp changes height from 'prevHeight' to 'height' gradually (smooth transition)
transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
}
else
{
transform.position = new Vector3(transform.position.x, height, transform.position.z);
}
}
private Vector3 GetAverageLocationOfSoliders()
{
var total = new Vector3();
foreach (var soldier in Soldiers)
{
total += soldier.transform.position;
}
return total / Soldiers.Count(); // Assuming Soldiers is List<Soldier>
}
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
}
The RotateAround part is working fine:
transform.RotateAround(GetAverageLocationOfSoliders(), axis, rotationSpeed);
But the slow down part is not working. It's not slowing down at all when the camera is facing the soldiers.
This is how I calculate the average transform.forward vector of all the soldiers:
private Vector3 GetAverageDirectionsOfSoliders()
{
var totalf = new Vector3();
foreach (var soldier in Soldiers)
{
totalf += soldier.transform.forward;
}
return totalf / Soldiers.Count();
}
Then inside the MoveInCircles method I did:
var F = GetAverageDirectionsOfSoliders();
var D = transform.position - GetAverageLocationOfSoliders();
var angle = Vector3.Angle(D, F);
if (angle < 5f)
{
rotationSpeed -= 0.1f;
}
But it's never getting to the line:
rotationSpeed -= 0.1f;
To rotate around all the soldiers:
Get the center of the all the soldiers (sum the world position of all soldiers and divide by N number of soldiers)
Get the maximum distance of the soldiers from the center
Rotate the camera about the center, and ensure that it is further than the max distance from the center
To slow the camera down when facing the soldiers' front:
Average out the transform.forward vector of all the soldiers. We call this vector F. This is fair since all your soldiers will typically be facing in the same general direction for this request to even make sense.
Calculate the direction D, which is the direction from the soldiers' center to the camera. This is easy: D = camera.transform.position - soldiersCenter
Finally, find the acute angle between D and F, using Vector3.Angle(). If this angle is lower than a certain threshold, decrease the moveSpeed of the camera.
The actual code is easy to write, but I'll let you practice. Let me know if you need any help
From the sounds of it, it seems you have it already able to orbit around a single soldier, so do to more than one, simply take the average of their locations.
Pseudo code:
transform.RotateAround(GetAverageLocationOfSoliders(), ...);
...
private static Vector3 GetAverageLocationOfSoliders
{
var total = new Vector3();
foreach(var soldier in Soldiers)
total += soldier.transform.position;
return total / Soliders.Count(); // Assuming Soldiers is List<Soldier>
}
Now, Vector3 might not have stuff like Vector3 += Vector3 or Vector3 / int, but if that's the case just create your own methods were you do that manually (adding vectorA.x + vectorB.x, and vectorA.x / num, etc.
As far as making the camera stop when it's in front, that's a bit trickier. You might want to do some checks first to make sure they all have the same rotation, and then check, each Update, if the look at rotation of the camera will point to one of the soldiers.
But, if you want it to slow down, then, instead of checking if the lookAtRotation can point to a soldier, check if it can point to some offset of the camera's rotation, like so:
Pseudo Code:
lookRotation(transform.Rotation + offset) // Use something like this to find Soldiers[0]
Then, if that finds one, you can use a Lerp on your speed to lerp back down to a speed of 0.
You will also have to maintain a List<Soldier> or Soldier[] somehow.

How can i calculate the rotation the player need to make to get to the next waypoint?

using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class Waypoints : MonoBehaviour
{
public GameObject[] waypoints;
public Transform target;
public float moveSpeed = 10f;
public float slowDownSpeed = 3f;
public float reverseSlowDownSpeed = 3f;
public float rotationSpeed = 1f;
private int targetsIndex = 0;
private Vector3 originalPosition;
private GameObject[] players;
public Transform reverseTarget;
private int reverseTargetsIndex = 0;
private Vector3 reverseOriginalPosition;
public bool random = false;
public bool getNextRandom = true;
// Use this for initialization
void Start()
{
waypoints = GameObject.FindGameObjectsWithTag("Blocks");
players = GameObject.FindGameObjectsWithTag("Player");
originalPosition = players[0].transform.localPosition;
}
// Update is called once per frame
void Update()
{
if (random == true)
{
RandomWayPointsAI();
}
else
{
WayPointsAI();
}
}
private void WayPointsAI()
{
if (targetsIndex == waypoints.Length)
targetsIndex = 0;
target = waypoints[targetsIndex].transform;
if (MovePlayer())
targetsIndex++;
}
private void ReverseWayPointsAI()
{
if (reverseTargetsIndex == 0)
reverseTargetsIndex = waypoints.Length - 1;
reverseTarget = waypoints[reverseTargetsIndex].transform;
if (MovePlayer())
reverseTargetsIndex--;
}
void RandomWayPointsAI()
{
if (random == true && getNextRandom)
{
int index = UnityEngine.Random.Range(0, waypoints.Length);
target = waypoints[index].transform;
getNextRandom = false;
}
getNextRandom = MovePlayer();
}
bool MovePlayer()
{
float distance = Vector3.Distance(players[0].transform.position, target.transform.position);
players[0].transform.localRotation = Quaternion.Slerp(players[0].transform.localRotation, Quaternion.LookRotation(target.position - players[0].transform.localPosition), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
players[0].transform.localPosition += players[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
players[0].transform.localPosition += players[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < target.transform.localScale.magnitude)
return true;
else
return false;
}
void DrawLinesInScene()
{
// draw lines between each checkpoint //
for (int i = 0; i < waypoints.Length - 1; i++)
{
Debug.DrawLine(waypoints[i].transform.position, waypoints[i + 1].transform.position, Color.blue);
}
// draw a line between the original transform start position
// and the current transform position //
Debug.DrawLine(originalPosition, players[0].transform.position, Color.red);
Debug.DrawLine(reverseOriginalPosition, players[1].transform.position, Color.red);
// draw a line between current transform position and the next waypoint target
// each time reached a waypoint.
if (target != null)
Debug.DrawLine(target.transform.position, players[0].transform.position, Color.green);
if (reverseTarget != null)
Debug.DrawLine(reverseTarget.transform.position, players[1].transform.position, Color.green);
}
void AddColliderToWaypoints()
{
foreach (GameObject go in waypoints)
{
SphereCollider sc = go.AddComponent<SphereCollider>() as SphereCollider;
sc.isTrigger = true;
}
}
}
There are two problems with the script.
If the Move Speed is set to 3 i need to set the Rotation Speed at least to the 10 if i will set the Rotation Speed to 3 or 4 or 5 the rotation will be too wide and it will take much time to the player to get back on track after rotating.
But if it's 10 it seems he is almost rotating on the place so it's not good either.
I want to be able to change the player rotation speed but also to keep him on the waypoints track at any time. I mean that each waypoint the player is reaching to that he will get to it's center. In this case cubes so not only to touch the waypoint but to get to it's center then moving to the next waypoint.
Another problem i see is with the RandomWayPointsAI()
When i change it to use the random method i'm not sure if it's just picking up random positions around the grid or if it's picking random blocks(Cubes).
But it's never getting to a cube it' getting close or between two or sometimes on a cube and same as before also on the random i want to make the random waypoints not just positions but blocks and to get to each random block center.
Center i mean like standing on it.
A glaring problem in the showcased code is the use of local positions and rotations. When moving and rotating characters and objects in the world you should use world space.
Localposition and localrotation are based on the position and rotation of the parent object. Using those to move your object across the world will cause weird problems.
I think the following answer has a pretty accurate explanation of that stuff https://teamtreehouse.com/community/global-space-vs-local-space

Add force to ball based on direction of camera

I am attempting to create a minigolf game but am having trouble solving how to calculate the direction to hit the ball. I am thinking that it is easiest to hit the ball in the forward direction that the camera is facing, but I get unexpected results after the first hit as the ball will not calculate the correct direction after hitting the first time. How can I set the direction for hitting the ball and then apply the force I am calculating?
This is my script attached to my ball object at the moment. Sorry for the messiness.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class Golfball : MonoBehaviour {
public GameObject ball = null;
public GameObject hole = null;
public GameObject cam = null;
public Text distance;
public Text score;
public Slider powerbar;
private int strokes = 0;
private bool isMoving = false;
private bool increasing = true;
private float distanceToHole;
public float minHitPower = 40.0f;
public float maxHitPower = 270.0f;
private float hitPower = 0;
private float powerIncrement = 5.0f;
private float powerMultiplier = 10;
private float ballRollTime = 0;
private Vector3 ballDir;
// Use this for initialization
void Start() {
distance.GetComponent<Text>().text = "Distance To Hole:" + distanceToHole;
ball.GetComponent<Rigidbody> ();
score.GetComponent<Text>().text = "Strokes:" + strokes;
}
// Update is called once per frame
void Update () {
//Allow the ball to be hit if the ball is not null, not currently moving, and the left mouse button is clicked.
if (ball != null) {
if (Input.GetButton("Fire1") && !isMoving) {
calculatePower ();
}
//Hit ball using power level and set ball to moving.
if (Input.GetButtonUp("Fire1"))
{/**********************************************
//Calculate direction to hit ball
ballDir = cam.transform.forward.normalized;
hitBall(hitPower);
isMoving = true;
}**********************************************/
//Detect when the ball stops
if (isMoving) {
ballRollTime += Time.deltaTime;
if (ballRollTime > 1 && GetComponent<Rigidbody> ().velocity.magnitude <= 0.5) {
GetComponent<Rigidbody> ().velocity = Vector3.zero;
GetComponent<Rigidbody> ().angularVelocity = Vector3.zero;
isMoving = false;
}
} else {
ballRollTime = 0;
}
}
//Calculate distance to hole
distanceToHole = Mathf.Round((Vector3.Distance(ball.transform.position, hole.transform.position) * 100f) / 100f);
distance.GetComponent<Text>().text = "Distance To Hole: " + distanceToHole;
Debug.DrawLine(ball.transform.position, ballDir, Color.red, Mathf.Infinity);
}
void calculatePower(){
//Increase power if it is less than the max power.
if (increasing)
{
if (hitPower < maxHitPower)
{
hitPower += powerIncrement * powerMultiplier;
increasing = true;
}
else if (hitPower >= maxHitPower)
{
increasing = false;
}
}
//Decrease power if power level is not increasing until the power hits the minimum level.
if(!increasing) {
//Debug.Log ("Not Increasing");
if (hitPower > minHitPower) {
//Debug.Log ("HitPower: " + hitPower);
hitPower -= powerIncrement * powerMultiplier;
} else if (hitPower <= minHitPower) {
increasing = true;
}
}
//Update the slider
powerbar.value = hitPower / powerMultiplier;
}
void hitBall (float power){
//Add force to the ball
//ball.GetComponent<Rigidbody>().AddForce(new Vector3(0, 0, power));
//Camera.main.transform.forward
ball.GetComponent<Rigidbody>().AddRelativeForce(ballDir * power);
//Increase stroke count
strokes++;
updateScore(strokes);
//Reset the power and power bar level to minimum default after hitting ball
hitPower = minHitPower;
powerbar.value = hitPower / powerMultiplier;
Debug.Log("HitPower Reset: " + hitPower);
}
void updateScore(int stroke)
{
score.GetComponent<Text>().text = "Strokes:" + stroke;
}
}
You'll have to apply the force like that:
ball.GetComponent<Rigidbody>().AddForce(ballDir * power, ForceMode.Impulse);
But you'll probably have to play with your 'power' variable.
Also note that you want to use impulse because you are hitting the ball and not applying force for some period of time.
The ballDir * power multiples the ballDir vector by your float number. You probably already know how it works, but here is the rundown:
scalarValue * new Vector(x0, x1, ..., xn) == new Vector(scalarValue * x0, scalarValue * x1, ... scalarValue * xn);]
You might also want to get rid of direction's y component so that your ball doesn't fly into the air (you are playing minigolf, right?)
ballDir.y = 0;

Categories

Resources