Hi, I’ve got a script, which, when the mouse is clicked and dragged from a sprite’s position, places waypoint spheres at regular intervals until the mouse button is released, at which point the sprite follows the drawn path, using the spheres as waypoints.
The problem I have, which I just can’t fathom a solution for, is that if you move the mouse pointer really quickly, the waypoints obviously have more space between them. I’ve tried to mitigate this with a combination of forcing a waypoint to be placed according to a mixture of a timer and a minimum/maximum distance between waypoints check but, there are currently two things wrong:
1: Even when moving the mouse slowly, spheres are sometimes place within the minimum distance from the last sphere, which shouldn’t be allowed.
2: When moving the mouse pointer quickly, I still can’t force waypoints to be drawn if, during a particular frame, the mouse pointer has passed the minimum gap distance.
The part of the script below that matters is the LateUpdate function, but I’ve included the whole thing for completeness. I’d appreciate any assistance, as the if statement that does the checks has been through every logical combination I can think of, but nothing so far has quote worked.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class AIscript : MonoBehaviour
{
//game objects (variables which point to game objects)
//private GameObject objPlayer;
//public GameObject objPlayerMesh;
private GameObject objCamera;
//input variables (variables used to process and handle input)
//private Vector3 inputRotation;
private Vector3 inputMovement;
//identity variables (variables specific to the game object)
public float moveSpeed = 3f;
private bool thisIsPlayer;
// calculation variables (variables used for calculation)
private Vector3 tempVector;
private Vector3 tempVector2;
private int i;
// animation variables (variables used for processing aniamtion)
public float animationFrameRate = 11f; // how many frames to play per second
public float walkAnimationMin = 1; // the first frame of the walk animation
public float walkAnimationMax = 10; // the last frame of the walk animation
public float standAnimationMin = 11; // the first frame of the stand animation
public float standAnimationMax = 20; // the last frame of the stand animation
public float meleeAnimationMin = 22; // the first frame of the melee animation
public float meleeAnimationMax = 30; // the last frame of the melee animation
public float spriteSheetTotalRow = 5; // the total number of columns of the sprite sheet
public float spriteSheetTotalHigh = 4; // the total number of rows of the sprite sheet
private float frameNumber = 1; // the current frame being played,
private float animationStand = 0; // the ID of the stand animation
private float animationWalk = 1; // the ID of the walk animation
private float animationMelee = 2; // the ID of the melee animation
private float currentAnimation = 1; // the ID of the current animation being played
private float animationTime = 0f; // time to pass before playing next animation
private Vector2 spriteSheetCount; // the X, Y position of the frame
private Vector2 spriteSheetOffset; // the offset value of the X, Y coordinate for the texture
public Vector2 spriteSheetOriginOffset;
// Path drawing variables
private bool isDragging = false;
private bool isMoving = false;
private bool isTravelling = false;
private int countDrag = 0;
private int countMove = 0;
private Vector3 mousePosition;
private Vector3 mousePoint;
private Vector3 pointCurrent;
private Vector3 pointStore;
private Vector3 targetWaypoint;
private Vector3 moveDirection;
private Quaternion wayRotation;
//private float damping = 6f;
private static int maxNumPathMarkers = 500;
private float lastMarkerX = 10;
private float lastMarkerZ = 10;
private List<float> posStoreX = new List<float>();
private List<float> posStoreZ = new List<float>();
private GameObject[] arrayPathMarker = new GameObject[maxNumPathMarkers];
public GameObject objPathMarker;
private Transform charSelected;
private bool isCharClickedOn = false;
// timer and target time between placement of path markers.
private float waypointTimer = 0;
private float waypointInterval = 0.001f;
// path marker distance restrictions
private float minMarkerGap = 0.5f;
// Initialization
void Start ()
{
//objPlayer = (GameObject)GameObject.FindWithTag ("Player");
objCamera = (GameObject)GameObject.FindWithTag ("MainCamera");
if (gameObject.tag == "Player") {
thisIsPlayer = true;
}
}
// Update is called once per frame
void Update () {
//FindInput ();
//ProcessMovement ();
if (thisIsPlayer == true) {
//HandleCamera ();
HandleAnimation ();
}
// Character travelling normally (no path drawn).
// Might need this if I allow player characters to move without a path
//if (!isTravelling) {
// transform.position += (transform.forward * moveSpeed * Time.deltaTime);
//}
}
void LateUpdate() {
waypointTimer += Time.deltaTime;
if (!isMoving) {
RaycastHit rayHit = new RaycastHit();
if(Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out rayHit)) {
mousePoint = rayHit.point;
}
if (Input.GetMouseButtonDown(0)) {
charSelected = rayHit.transform;
if (charSelected == this.transform) {
isCharClickedOn = true;
}
if (isCharClickedOn) {
OnTouchBegin(mousePoint);
}
}
else if (Input.GetMouseButton(0)) {
if (isDragging
(waypointTimer >= waypointInterval !((Mathf.Abs(mousePoint.x - lastMarkerX) < minMarkerGap) (Mathf.Abs(mousePoint.z - lastMarkerZ) < minMarkerGap))
(Mathf.Abs(mousePoint.x - lastMarkerX) >= minMarkerGap) || (Mathf.Abs(mousePoint.z - lastMarkerZ) >= minMarkerGap))) {
Debug.Log(waypointTimer);
if (waypointTimer >= waypointInterval) waypointTimer = 0;
// check in case mouse is down when object travelling is set to false
if (countDrag < maxNumPathMarkers) {
OnTouchMove(mousePoint);
}
else {
isDragging = false;
isTravelling = true;
}
}
}
else if (Input.GetMouseButtonUp(0)) {
// check in case mouse is released when object travelling is set to false
if (isDragging) {
//RaycastHit rayHit = new RaycastHit();
//if(Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out rayHit)) {
// mousePoint = rayHit.point;
//}
OnTouchEnd(mousePoint);
}
}
}
// move gameObject
if ((isTravelling) (posStoreX[countMove] != 0)){
// move object along path
targetWaypoint = new Vector3(posStoreX[countMove], 0, posStoreZ[countMove]);
moveDirection = targetWaypoint - transform.position;
// IS THIS THE BIT THAT CAUSES SPHERES TO BE LEFT BEHIND?
if(moveDirection.magnitude < 1) {
// remove current path marker
Destroy (arrayPathMarker[countMove]);
countMove++; // next waypoint position
} else {
transform.position += moveDirection.normalized * moveSpeed * Time.deltaTime; // move gameObject
transform.LookAt(targetWaypoint);
//wayRotation = Quaternion.LookRotation(targetWaypoint - transform.position);
//transform.rotation = Quaternion.Slerp(transform.rotation, wayRotation , Time.deltaTime * damping);
}
if (countMove >= posStoreX.Count) {
finishTravelling (); // stop at end of path
}
} else {
countMove = 0;
}
}
//void FindInput ()
//{
// if (thisIsPlayer == true) {
// FindPlayerInput ();
// } else {
// FindAIinput ();
// }
//}
void OnTouchBegin (Vector3 pointCurrent) {
countDrag = 0;
posStoreX.Clear();
posStoreZ.Clear();
AddSplinePoint(pointCurrent);
isDragging = true;
isMoving = false;
}
void OnTouchMove (Vector3 pointCurrent) {
if ((isDragging) (countDrag <= maxNumPathMarkers)) {
AddSplinePoint(pointCurrent);
isTravelling = false;
}
else {
isDragging = false;
isMoving = true;
}
}
void OnTouchEnd (Vector3 pointCurrent) {
isTravelling = true;
isDragging = false;
isMoving = true;
if (isCharClickedOn) {
isCharClickedOn = false;
charSelected = null;
}
}
void AddSplinePoint (Vector3 pointStore) {
// store co-ordinates
if (posStoreX.Count > 0) {
lastMarkerX = posStoreX[posStoreX.Count - 1];
lastMarkerZ = posStoreZ[posStoreZ.Count - 1];
}
posStoreX.Add(pointStore.x);
posStoreZ.Add(pointStore.z);
// show path : Instantiate and load position into array as gameObject
arrayPathMarker[countDrag] = (GameObject) Instantiate(objPathMarker, new Vector3(pointStore.x, -0.9f, pointStore.z), transform.rotation);
// next position
countDrag ++;
}
void finishTravelling () {
countMove = 0;
isMoving = false;
if (isCharClickedOn) {
isCharClickedOn = false;
charSelected = null;
}
isTravelling = false;
}
//void FindPlayerInput ()
//{
// find vector to move
// CHANGE THIS TO THE LOCATION OF THE NEXT PATHMARKER
//inputMovement = new Vector3 (Input.GetAxis ("Horizontal"), 0, Input.GetAxis ("Vertical"));
// find vector to the mouse
//tempVector2 = new Vector3 (Screen.width * 0.5f, 0, Screen.height * 0.5f);
//tempVector = Input.mousePosition;
//tempVector.z = tempVector.y; // input mouse position gives us 2D coordinates, I am moving the Y coordinate to the Z coorindate in temp Vector and setting the Y coordinate to 0, so that the Vector will read the input along the X (left and right of screen) and Z (up and down screen) axis, and not the X and Y (in and out of screen) axis
//tempVector.y = 0;
//Debug.Log (tempVector);
//inputRotation = tempVector - tempVector2; // the direction we want face/aim/shoot is from the middle of the screen to where the mouse is pointing
//}
//void FindAIinput ()
//{
//}
//void ProcessMovement ()
//{
// rigidbody.AddForce (inputMovement.normalized * moveSpeed * Time.deltaTime);
// transform.rotation = Quaternion.LookRotation (inputRotation);
// transform.eulerAngles = new Vector3 (0, transform.eulerAngles.y + 180, 0);
// transform.position = new Vector3 (transform.position.x, 0, transform.position.z);
//}
void HandleCamera ()
{
objCamera.transform.position = new Vector3 (transform.position.x,
15, transform.position.z);
objCamera.transform.eulerAngles = new Vector3 (90, 0, 0);
}
void HandleAnimation () // handles all animation
{
FindAnimation ();
ProcessAnimation ();
}
void FindAnimation ()
{
//if (inputMovement.magnitude > 0) {
if (isTravelling) {
currentAnimation = animationWalk;
} else {
currentAnimation = animationStand;
}
}
void ProcessAnimation ()
{
animationTime -= Time.deltaTime; // animationTime -= Time.deltaTime; subtract the number of seconds passed since the last frame, if the game is running at 30 frames per second the variable will subtract by 0.033 of a second (1/30)
if (animationTime <= 0) {
frameNumber += 1;
// one play animations (play from start to finish)
if (currentAnimation == animationMelee) {
frameNumber =
Mathf.Clamp (frameNumber, meleeAnimationMin, meleeAnimationMax + 1);
if (frameNumber > meleeAnimationMax) {
/* if (meleeAttackState
== true) // this has been commented out until we add enemies that will attack with their evil alien
claws
{
frameNumber =
meleeAnimationMin;
} else {
currentFrame =
frameStand;
frameNumber =
standAnimationMin;
}*/
}
}
// cyclic animations (cycle through the animation)
if (currentAnimation == animationStand) {
frameNumber =
Mathf.Clamp (frameNumber, standAnimationMin, standAnimationMax + 1);
if (frameNumber > standAnimationMax) {
frameNumber = standAnimationMin;
}
}
if (currentAnimation == animationWalk) {
frameNumber =
Mathf.Clamp (frameNumber, walkAnimationMin, walkAnimationMax + 1);
if (frameNumber > walkAnimationMax) {
frameNumber = walkAnimationMin;
}
}
animationTime += (1 / animationFrameRate); // if the animationFrameRate is 11, 1/11 is one eleventh of a second, that is the time we are waiting before we play the next frame.
}
spriteSheetCount.y = 0;
for (i=(int)frameNumber; i > 5; i-=5) { // find the number of frames down the animation is and set the y coordinate accordingly
spriteSheetCount.y += 1;
}
spriteSheetCount.x = i - 1; // find the X coordinate of the frame to play
// find the X and Y coordinate of the frame to display
spriteSheetOffset = new Vector2 (1 - (spriteSheetCount.x / spriteSheetTotalRow), 1 - (spriteSheetCount.y / spriteSheetTotalHigh));
spriteSheetOffset += spriteSheetOriginOffset;
renderer.material.SetTextureOffset ("_MainTex", spriteSheetOffset); // offset the texture to display the correct frame
}
}
The code is a mish mash of a couple of tutorial scripts, and my own additions.