Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Scripting Help Required, Player Slide Into Position ?

Discussion in 'Scripting' started by Deleted User, Sep 24, 2014.

  1. Deleted User

    Deleted User

    Guest

    Hi,

    ‘Artist’ requiring scripting help ! :)

    I’m trying to put together a simple prototype for a game I’m designing, and in order to showcase it, I would like to develop a very simple scenario that has some basic gameplay in it.

    Let’s say I have a cube, I want it to’

    ( a ) sit and wait for the user to interact, i.e. wait for a mouse click
    ( b ) once the mouse is clicked, the cube ( player ) has velocity applied in the given direction
    ( c ) the cube, player, slides to the next checkpoint ( this could be any given distance )
    ( d ) cube, player, MUST be central on the next checkpoint
    ( e ) at next checkpoint, we repeat process ( a ) onwards, i.e. wait for user to interact

    To set the scene, a picture is worth a 1000 words so : -



    My ideas so far : -

    Obviously, use some sort of trigger event, which I script, I suspect I will need FOUR scripts, each for the chosen direction I would like to send the player in once they land on a given checkpoint, i.e 4 scripts that send the player Left, Right, Up or Down, which I will then drag and drop on the required checkpoints, depending on which direction I want to send the player in next.

    Potential problems I foresee, if I create a trigger event, how do I ‘stop’ my cube at the centre of it’s destination, rather than the trigger event simply ‘stopping’ it once it enters the trigger zone ? Possibly use a smaller trigger zone, etc, don’t know if this is logical or anything, would appreciate any help I can get here.

    My art skills are better than my programming skills, but I’m willing to delve into script and get my hands dirty, as I do understand the basics, so any help or point me in the right direction would be thankfully received.

    They are other elements to add into the prototype, but these can come at a later date, right now I just want to get some basic functionality up and running.
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Is any rigidbody or CharacterController involved in your prototype?
    If it's only a simple object without any of both, you can use Vector3.MoveTowards or Vector3.Lerp for it.

    *edit
    Latter one is linear interpolation. By mistake, many people use it not the way it's supposed to be used but this can add a slow-down effect the closer you get to the target. Just in case you don't want completely linear movement. You may also have a look at Vector3.Slerp or Vector3.SmoothDamp.
     
    Last edited: Sep 24, 2014
  3. Deleted User

    Deleted User

    Guest

    Hi, Suddoha

    No CharacterController involved, I want to keep it fairly simply to control, hence the need to do it all in script. Plus, I'm learning as I go ! :)

    Rigidbodies will probably be involved, as the player ( when I add it at a later date ), will also encounter obstacles / enemies between points, so the player will need to judge best time to tap or click. I think rigidbodies would work best in this instance to trigger events, i.e Gameover, when the user collides with an obstacle rather than a checkpoint ? Although if not, any other suggestions would be great ! :)

    I've been playing around with setting my cube to the right orientation ( let's pretend the cube has a cute face on the front ), so I know it travels in the right direction ), then applying transform.Translate with Vector3.forward at a controlled speed ( that I've made public and can change on a whim as required ).

    I will look into your Vector3.MoveTowards method and research that, the lerp method seems interesting from the slowdown ( ease in / out ? ) movement, but would I need to define exact distances to travel, I need to look into that to.

    Thanks, you've given me some stuff to look into anyway for now. So thumbs up for that.

    I'm curious how would you go about, or anyone else really, the 'making sure the cube' hits the centre point of the checkpoint location, rather than coming to a rest as soon as it hits the trigger zone ?

    Thanks
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    You wouldn't really use triggers in order stop there. At least i wouldn't do that, because it's not a nice way to control the kind of movement you desire.
    Lerp is actually linear interpolation. If you use it the 'wrong way' you'll start at a certain speed and slow down. Theoretically you'd never come to the final position but that's easy to control with conditions (distance checks).

    This is actually the wrong use but it's also a nice effect:
    Code (CSharp):
    1. transform.position = Vector3.Lerp(transform.position, target.position, 0.1f);
    The method calculates the distance between the two vectors and multiplies it by the third paramter (percentage to move).
    Example with simple values:
    Start position is (0,0,0), target position is (0,0,10). Third paramter is 0.1 (10%).
    The method will return (0,0,1) and assign it to transform.position. You've moved 1 unit in z-direction.
    Now the start position is (0,0,1), target position is still (0,0,10). You'll move (10-1)*0.1 = 0.9 units in z-direction now. You end up on (0,0,1.9). And so on, that's why you'd theoretically never reach the target position.
    You'd slow down the closer you get.

    If you want an ease in/out effect, you'd rather want to have a look at Spherical Linear Interpolation, short: Slerp.

    As for the collisions with obstacles, if you don't want to have physical collision behaviour (applying forces etc) it's also a nice and easy way to use a CharacterController.

    In order to change the direction that the object faces, you can use similar methods provided by the Quaternion struct (Lerp, Slerp, RotateTowards, LookRotation) or Transform.LookAt.
     
  5. Deleted User

    Deleted User

    Guest

    Brilliant, lot's of fantastic information, Suddoha, so thank you for taking the time to help out and research this.

    I've a lot to look into here, I'm going to delve into this tonight, and I'll probably post my results here as and when I get something nice up and running, and then see if I can improve upon it anyway.

    Kind regards.
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    No problem, don't hesitate to ask again if anything doesn't work as expected.
     
  7. Deleted User

    Deleted User

    Guest

    Hi,

    Been switching between creating the artwork and scripting, ran into a few snags,

    so, first up, a snippet of code from the player ( yellow cube )
    --------------------------------------------------------------------------------------------
    Code (CSharp):
    1.     void Update () {
    2.  
    3.         // Check For Left Mouse Click
    4.  
    5.         if (Input.GetMouseButtonDown(0)) {
    6.             print("Yes, The Button Works!");
    7.             transform.Translate(Vector3.forward * Time.deltaTime * playerSpeed);
    8.         }
    9.     }
    --------------------------------------------------------------------------------------------

    I had this working up until I added the check for the GetMouseButtonDown left click, now it just moves forward frame by frame, how can I get it back to just moving forward continually when I check for the 1st mouse click. So, wait for click, then move forward, any ideas ?

    secondly, before I encountered the code problem above, I had my trigger code set up, so the yellow cube was in motion, and when it hits the collider, rotates and carries on moving in the correct direction, except, I never EVER get to the centre of the collider, as soon as I literally HIT the collider, my trigger event 'happens', so it rotates and carries on it's motion path without ever entering the collider area. I NEED this somehow not to trigger until the player is in the dead centre of the collider box, NO idea how I am going to accomplish that though ?

    Any help appreciated. :/
     
    Last edited by a moderator: Sep 26, 2014
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    For the first problem, add a boolean property (e.g. "bool inMotion = false". Then rework your code so that the mouse click sets this to true, and you move only when this is true, like so:

    Code (CSharp):
    1. void Update () {
    2.         // Check For Left Mouse Click
    3.         if (Input.GetMouseButtonDown(0)) {
    4.             print("Yes, The Button Works!");
    5.             inMotion = true;
    6.         }
    7.         if (inMotion) {
    8.             transform.Translate(Vector3.forward * Time.deltaTime * playerSpeed);
    9.         }
    10.     }
    For the second one, you have a couple options. You could reduce the size of the trigger collider so that it doesn't fire until you are in the center — or so close that you can just set it at the center without the player noticing. Or you could lose the colliders, and just check in code; the same code that does the movement above could see whether the distance to the center is less than the distance it's about to move, and if so, just set it to the center and trigger whatever comes next.

    (I'm assuming here that it's not possible to "miss" the target by going in the wrong direction.)
     
  9. Deleted User

    Deleted User

    Guest

    Hi, JoeStrout

    Thanks for your help, I think I understand how this works now, it certainly works in game anyway !

    By my understanding, the boolean is set to false when we declare it, when the Mouse is clicked, the boolean is then set to True, and if True ( ? ) then perform the Translation.

    What's confusing my limited understanding here is the if ( inMotion ) segment, does this mean it performs the translation because we have set it to be true ? Does this mean once we set the cube in motion, it will never ever reset back to a false state ( i.e. stop it ), unless I set it to false via my trigger script on the collider ( which is what I want actually ) ?


    -----------------

    It seems the trigger event with the collider is going to take a lot more research to get the result I'm after ( making sure I hit the centre ), but thanks for your suggestions, certainly lots to look into anyway and try out. Not sure how to check via code, wanted to keep this simple, hence the trigger event within a collider, but willing to look into it to see what's possible.

    No, it's not possible to head off in a wrong direction, the game prototype is basically more about avoiding the obstacles in between checkpoints, so once you set the yellow cube ( player ) in motion it does not stop until it hits the next checkpoint, or it encounters an obstacle / enemy on it's route. As you hit each checkpoint, I rotate the player so that's always facing the right direction, basically it's a game about timing, clicking ( or tapping the screen ) at the right time to avoid what obstacles may be in the way as you go.

    Thanks.
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Yes, I think your understanding of the "inMotion" flag is correct. Once set to true, your object will translate on every frame until something (such as the collider trigger) sets it to false.

    But since you're struggling with how to detect when you've reached the goal, maybe another approach is in order. When you start this motion — that is, when there is a mouse click — do you have some way of knowing where the next target position is? You say you're rotating the cube to face the target; how do you do that?

    If you can get the end (target) position, then instead of using translate and hoping for some sort of trigger, you could use Lerp (or even SmoothStep for a nice accelerate/decelerate effect) to move the cube. Something like this (untested, off-the-cuff code):
    Code (CSharp):
    1. Vector3 targetPos;   // set this up somehow
    2. float moveDuration = 2f;
    3.  
    4. Vector3 startPos;
    5. float startTime;
    6. bool inMotion = false;
    7.  
    8. void Update() {
    9.     // Check for Left Mouse Click
    10.     if (Input.GetMouseButton(0)) {
    11.         // Start moving!
    12.         inMotion = true;
    13.         startPos = transform.position;
    14.         startTime = Time.time;
    15.     }
    16.    
    17.     if (inMotion) {
    18.         // Keep on moving
    19.         float t = (Time.time - startTime) / moveDuration;
    20.         if (t >= 1) TargetReached();
    21.         t = Mathf.SmoothStep(0, 1, t);    // smooth start/end
    22.         transform.position = Vector3.Lerp(startPos, targetPos, t);
    23.     }
    24. }
    25.  
    26. void TargetReached() {
    27.     // Do whatever you want here.
    28. }
     
  11. Deleted User

    Deleted User

    Guest

    Hi, Joe

    The trigger event currently works as it does what it's supposed to do, I just currently wanted it 'not' to trigger my player until it was dead centre on the checkpoint, and waiting for the next mouseClick event before it heads off again.

    So a snippet of code from my trigger event for a checkpoint collider : -

    Code (CSharp):
    1.     // Check Player Has Entered Checkpoint Collider
    2.  
    3.     void OnTriggerEnter (Collider wFMainPlayer) {
    4.         if (wFMainPlayer.gameObject.tag == "Player") {
    5.             wFMainPlayer.transform.Rotate(0,270,0);
    6.  
    7.             //  Stop Current Motion - haven't figured this out since we added inMotion boolean
    8.  
    9.             // Play Chosen 'Mecanim' Clip - removed for the sake of clarity
    10.  
    11.             // Play 'Supporting' Audio - removed for the sake of clarity
    12.  
    13.             // Play 'Supporting' Particles - removed for the sake of clarity
    14.  
    15.         }
    16.     }
    Note : rotation is set here as 0,270,0, for my current 'testing phase, I simply have a square based grid, 5x5, with a checkpoint in every corner to make things easy, hence the rotation values here.

    The way the game prototype works, a lerp function would not work as well, as I need to keep the player at a constant speed, easing in and easing out could create non desirable encounters with obstacles / enemies on each 'track'.
     
    Last edited by a moderator: Sep 26, 2014
  12. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    I still think you're better off using Lerp here than the velocity/trigger approach. It's much simpler and more reliable.

    If you don't want the ease-in/ease-out, then simply remove the "t = Mathf.SmoothStep(0, 1, t)" line from the code I posted.
     
  13. Deleted User

    Deleted User

    Guest

    Ok, thanks Joe, it's the weekend, so I've got a lot of time to look into this and test further, I'll look into your suggestions, and post here any findings / problems I come across, once again, thanks for all your time and effort.

    Regards
     
  14. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Well, i linked all the methods which may help you in my previous posts. And i still claim that you are better off without a trigger, at least if they are not in use for other controls. :p
    When the mouse click happens, you should start moving your object. Keep checking the distance between your current position and the targets position and set a threshold, As soon as you are within the threshold, don't move it again with the normal translation method that you use, instead set the objects position to the target's position.
    This way you're exactly at thr position you desire.

    If you provided some more information about how you want to turn the player, i could also add some sample code. E.g. do you want it to turn all the way in one frame? Do you want to change the direction over time while you're still moving? Do you want to stop at the target position first and turn smoothly to the next target afterwards?
     
  15. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    He wants to move from one cube with the red sqaure on it to the next one, not through the air :p
     
  16. Deleted User

    Deleted User

    Guest

    Hi, Suddoha

    Yes, you did ! But my limited knowledge of scripting has me trying to do what I need in the least amount of steps ! :)

    Here's a quick flow of how I expect the game to work :

    Imagine a boat / fish, or something similar sitting in a canal, surrounded by walls, in the first example I posted, the 'canal' would be the black / white squares, so

    1 ) Game starts, player is at beginning, waiting for user input ( mouse click )
    2 ) Mouse click happens, player is set in motion ( at constant speed ), and unstoppable until next checkpoint is reached.
    3 ) At next checkpoint, player is immediately set to face correct direction, (left, right, up or down ) and will then go through any extraneous 'extras' ( particles, idle animation, sound effect to signify checkpoint arrival ), player sits and waits for next mouse click event.
    3 ) Player 'dies' should it hit any obstacle / enemy on route ( for instance, a block that moves up and down and blocks the way temporarily, so good timing is required for user input ), so if die, game restarts back at initial starting point, no matter how far you have gotten, infinite retries !
    4 ) if you make it through every checkpoint and manage to avoid every obstacle, player wins that level, simple A to B 'track', level becomes 'completed', onto next level....
     
  17. Deleted User

    Deleted User

    Guest

    Hi, bbQsauce

    yes, that's right, the 'click' ( or tap for mobile ) literally just initiates the players motion, it should be a '1 button' game to play, if you know what I mean. Once in motion, you will proceed to the next checkpoint automatically, but there will be moving obstacles in your path, so you will need to time your 'clicks' perfectly to avoid these. I hope that makes sense ? :)
     
  18. Deleted User

    Deleted User

    Guest

  19. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I thought you wanted him to get the closest position to his mouse click?! If he can only walk forward, but the closest-to-mouse-click position is not straight in front of him, he'd be floating through the air. (E.g. the second position in his picure shown above).
     
  20. Deleted User

    Deleted User

    Guest

    Interesting... I'm definitely going to test this method.

    Thanking all for their advices, this is really helping me, didn't just want to reskin one of the game templates available on the appstore, wanted to at least try to make my own game ! ;)
     
  21. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I know it can be controlled, but he wants to follow a simple 'path', why all that raycasting, finding nearest possible position and determine whether he can move or not? Maybe i've completely missed something here, but i'd go for something like this ... it has to be adjusted for your needs especially when using rigidbodies or characterController @playmint but i think you'll get the idea and for a prototype it's simple enough.

    Code (CSharp):
    1. public Transform[] targets;
    2.  
    3.     public float speed = 5;
    4.     private bool canMove;
    5.     private int nextTargetsIndex;
    6.  
    7.     void Start()
    8.     {
    9.         canMove = true;
    10.         nextTargetsIndex = 0;
    11.     }
    12.  
    13.     void Update()
    14.     {
    15.         if (Input.GetMouseButtonDown(0) && canMove)
    16.         {
    17.             canMove = false;
    18.             StartCoroutine(Move());
    19.         }
    20.     }
    21.  
    22.     IEnumerator Move()
    23.     {
    24.         while(transform.position != targets[nextTargetsIndex].position)
    25.         {
    26.             transform.position = Vector3.MoveTowards(transform.position, targets[nextTargetsIndex].position, speed * Time.deltaTime);
    27.             yield return null;
    28.         }
    29.         LastPositionCheck();
    30.     }
    31.  
    32.     void LastPositionCheck()
    33.     {
    34.         if (nextTargetsIndex < targets.Length - 1)
    35.         {
    36.             nextTargetsIndex++;
    37.             canMove = true;
    38.         }
    39.         else
    40.         {
    41.             Debug.Log("Reached the final target position");
    42.             // or whatever
    43.         }
    44.     }
     
  22. Deleted User

    Deleted User

    Guest

    Wow, that's brilliant Suddoha.

    I'm not even going to pretend I understand everything that's happening here just yet, I've simply copied and pasted it, and it works a charm, once I set up the empty Gameobjects into position anyway.

    Seriously, I'm going to study this until I understand 'everything' that's happening and why, I mostly understand it, but obviously want to fully understand it all.

    I'd like to thank all contributors who've helped here also, as it's been an interesting learning experience, and still is, I'm sure I have still got lots left to do and no doubt will post more questions in the near future.

    Thanks everyone.

    Regards

    Steve
    playmint
     
  23. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Note that this script does not handle the rotation yet. But for that you'd simply need to add one or two lines at the end of the coroutine or better, in the if part of the position check method since there's no next position to face when you've finished.
    Also, if you need some further explanation what happens in which line, let us know.

    There are actually different ways to achieve that (like always), but that's one very simplified version for the prototype.
     
  24. Deleted User

    Deleted User

    Guest

    thanks Suddoha, I've already added the rotation part. And thanks for the offer to help with any parts I might be struggling with, as I've said before, I'm primarily an artist, but I do have some basic programming experience, so it's been useful here to some extent that I understand some of the fundamentals, otherwise I would've been completely lost ! :eek:

    No problem, bbQsauce, all the feedback has been helpful, made me look into alternative methods and ways of getting what I wanted.

    :cool:
     
  25. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Could have followed, but i have not (*cough* my posts from yesterday *cough*). Anyway, don't tell me you want to argue about that know :confused:
     
  26. Deleted User

    Deleted User

    Guest

    Changed my messy rotation code and replaced it with

    Code (CSharp):
    1.             transform.LookAt(targets[nextTargetsIndex].position);
    Seems to work great and points towards the next Target as expected, but posting here in case my logic doesn't make sense. :oops:
     
    Last edited by a moderator: Sep 26, 2014
  27. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Uhu... think what you want. :rolleyes: