Search Unity

Help with simple 2D

Discussion in '2D' started by Sir_NoFace, Feb 16, 2016.

  1. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hello Folks and thanks for looking into this post.

    I tried to find tutorials or similar to what I need but unfortunately I was not lucky at all.
    Here is what I want to do:

    Randomly spawn 10 black and 10 white balls or squares in a container and make them move in random directions, same or different speeds, bouncing off the walls.

    And here comes the action:
    If black ball hits white ball, both balls splits in 2 smaller balls.
    If same color hits same color, they merge to a bigger ball.

    I would be very thankful if someone could make me an tutorial or link me to tutorials that could help me out with that.

    Thanks a lot in advance!

    Tom
     
  2. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
  3. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hey Hyblademin
    Yes thanks, I've been in there but either I'm blind or I can find a tutorial that explains how to merge 2 balls together or separates them. The thing is I'm new to Unity and CS coding, but thanks for the Link :)
     
    ThreeMusketeers likes this.
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    The easiest way I would think would be to use the physics system. Give your balls a collider2D and a rigidbody2D, and make your container walls using gameobjects with colliders. If your balls are moving very fast, set them to have Continuous Collision.

    Then give the ball a script to handle the collision events.

    See the "Messages" section of the docs here:
    http://docs.unity3d.com/ScriptReference/Collider2D.html

    "OnEnter", you will have the ball check the other colliding ball's color, or type, or however you want to distinguish between the two. Could be a tag, up to you.

    If they are the same type, have the bigger ball destroy the other one, and set its own scale bigger.

    If they are different, then each ball will have to Instantiate a new ball, and set the scale of the new ball as well as itself smaller than the original.

    That should give you some things to look into. Try searching "Unity 5 2D physics" into youtube or google.
     
    Deleted User and Sir_NoFace like this.
  5. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Thanks a bunch Jeff, that got me on the right path.
    Now im quiet a noobie to scripting with C#, and I cant find a code making the balls start off moveing into random angles at the start, in a straight line inside the container (bird view, no gravity) until they hit a wall and bounce off etc.

    I searched a lot around google and youtube allready, but most videos I found have the objects bounce with gravity instead of floating around.

    I can imagine there is a very simple code for this?

    Cheers
     
  6. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Hey, if you want your objects to slow down over time, use Drag, otherwise set it to 0. If you want them to behave with gravity, then use the Gravity Scale, otherwise set to 0. These are settings on the Rigidbody2D.

    To start them off, in their script you could have a Start() function like this:
    Code (CSharp):
    1. float startForce = 10f;
    2. float x = Random.Range(-1, 1);
    3. float y = Random.Range(-1, 1);
    4. Vector2 randomDirection = new Vector2(x, y).normalized;
    5. GetComponent<Rigidbody2D>().AddForce(randomDirection * startForce, ForceMode2D.Impulse);
    As long as the colliders are not set to be triggers, they should hit the walls of the container. To make them bounce, give the colliders the "Bounce" physic material.

    If you don't have any imported into your project, you can easily make one by going to your project panel, and then Create -> Physics2D Material. Name it Bounce, give it low friction and high bounciness, then assign it to your colliders.
     
    Last edited: Feb 17, 2016
  7. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hey Jeff

    at about the same time I managed to make them move:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BallScript : MonoBehaviour {
    5.  
    6.     public float ballVelocity = 3000;
    7.  
    8.     Rigidbody2D rb;
    9.  
    10.     bool isPlay;
    11.     int randInt;
    12.  
    13.  
    14.  
    15.     void Awake ()
    16.     {
    17.         rb = gameObject.GetComponent<Rigidbody2D> ();
    18.         randInt = Random.Range (1, 3);
    19.  
    20.     }
    21.  
    22.     void Update()
    23.     {
    24.          if (Input.GetMouseButton(0) == true && isPlay == false)
    25.             {
    26.                 transform.parent = null;
    27.                 isPlay = true;
    28.                 rb.isKinematic = false;
    29.             if (randInt == 1)
    30.             {
    31.  
    32.                 rb.AddForce(new Vector3(ballVelocity, ballVelocity, 0));
    33.             }
    34.  
    35.             if (randInt == 2)
    36.  
    37.              {
    38.                 rb.AddForce(new Vector3(-ballVelocity, -ballVelocity, 0));
    39.             }
    40.  
    41.         }
    42.  
    43.     }
    44.  
    45. }


    That works perfectly fine, got it from a Pong tutorial, all I need to do now is remove the action on click, instead make it move when starting the game.

    Now all I need is to make the Objects split or Merge on hit and increase size when merging or decresing size when splitting in 2.

    Thanks for your help jeff! :)
     
    LiterallyJeff likes this.
  8. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    I took your code now, works a lot better, thanks again!

    Now I have this here to make them merge and destroy the other one:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class DestroyCircles : MonoBehaviour
    5. {
    6.     GameObject ballWhite;       //used to declare the white ball
    7.     GameObject ballBlack;       //used to declare the black ball
    8.  
    9.     void Start()
    10.     {
    11.         ballWhite = GameObject.Find("ballWhite");   //whiteball object must be named 'ballWhite' and also the tag
    12.         ballBlack = GameObject.Find("ballBlack");   //blackball object must be named 'ballBlack' and also the tag
    13.  
    14.     }
    15.  
    16.     void OnCollisionEnter(Collision objHit)
    17.     {
    18.         if (ballWhite && objHit.gameObject.tag == "ballWhite")
    19.         {
    20.             Destroy(objHit.gameObject);
    21.             transform.localScale += new Vector3(0.1F, 0, 0);
    22.         }
    23.     }
    24. }


    I have 20 Black ones and 20 White balls, for some reason this wont work tho... what did I do wrong?
     
  9. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
  10. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    True, forgot about that. Now it kinda works, when they colide, the kill each other... but the other one wont grow. all 20 of them have the same code on it. how can I make it random, only one is supposed to delete and the other one grow...
    Can find any code for that anywhere...
     
  11. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    You will rarely find the exact tutorial you need for your specific game mechanics. You have to learn some scripting techniques so you can think through a problem and make your own solutions using the framework that Unity provides.

    One of the balls will always have it's OnEnter called first in a collision. They can't happen at the exact same time, one will always go first.

    In this code I'm using an Enum "BallType" as a nice way to keep track of the ball's type. If you're not familiar with enums, they are super helpful. An Enum is just a way to create a list of options. In this case, I'm using it for the different types a ball can be.

    The variable "type" is a "BallType" variable, and can hold one of the entries from the Enum, and is assigned in the inspector for the object. Enums are shown as a drop-down list in the inspector.

    Then in the OnCollisionEnter function I added a type comparison and scale comparison, and based on those I choose either Absorb or Split. This will also split the other one.

    To keep the new objects from immediately interacting, and the existing objects from re-interacting, I added a cooldown for the interaction which starts on Absorb and Split.

    Let me know if you have trouble following anything I've done here, and I'd be happy to explain further. It would be in your best interest to really try and understand everything that's happening and why and how.

    Also just a warning, I haven't tested this code but it should work.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. // types of balls
    5. public enum BallType {
    6.     White,
    7.     Black,
    8. }
    9. public class DestroyCircles : MonoBehaviour {
    10.     // set in the inspector
    11.     [Tooltip("Type of ball.")]
    12.     public BallType type;
    13.     [Tooltip("The timeout between collision interactions.")]
    14.     public float interactionCooldown = 1f;
    15.  
    16.     private float timeSinceInteraction;
    17.     private bool canInteract;
    18.  
    19.     // called every frame by Unity
    20.     private void Update() {
    21.         if(!canInteract) {
    22.             // add the time since last frame
    23.             timeSinceInteraction += Time.deltaTime;
    24.             // if the timer surpasses the cooldown...
    25.             if(timeSinceInteraction >= interactionCooldown) {
    26.                 // this object can interact again
    27.                 canInteract = true;
    28.             }
    29.         }
    30.     }
    31.  
    32.     public void StartInteractionTimer() {
    33.         timeSinceInteraction = 0;
    34.         canInteract = false;
    35.     }
    36.  
    37.     public void Split() {
    38.         // half the scale (might get dangerous if things get too small)
    39.         transform.localScale *= 0.5f;
    40.         // make new copy of this ball
    41.         DestroyCircles newBall = Instantiate(gameObject).GetComponent<DestroyCircles>();
    42.         // cooldown for this object
    43.         StartInteractionTimer();
    44.         // cooldown for the new object
    45.         newBall.StartInteractionTimer();
    46.     }
    47.  
    48.     private void AbsorbOther(GameObject other) {
    49.         // disable and destroy the other one
    50.         other.gameObject.SetActive(false);
    51.         DestroyImmediate(other);
    52.         // add size to this object
    53.         transform.localScale += new Vector3(0.1f, 0, 0);
    54.         // cooldown for this object
    55.         StartInteractionTimer();
    56.     }
    57.  
    58.     private void OnCollisionEnter2D(Collision2D collision) {
    59.         // if this object can interact
    60.         if(canInteract) {
    61.             // get the other object's collider script
    62.             DestroyCircles other = collision.gameObject.GetComponent<DestroyCircles>();
    63.             if(other != null) {
    64.                 // true if the types match
    65.                 bool sameType = type == other.type;
    66.                 // nice way to compare the sizes of each Vector3 scale
    67.                 bool biggerOrSame = transform.localScale.sqrMagnitude >= other.transform.localScale.sqrMagnitude;
    68.  
    69.                 // if they're the same type
    70.                 // and this one is bigger or the same size
    71.                 if(sameType && biggerOrSame) {
    72.                     // absorb the other one
    73.                     AbsorbOther(other.gameObject);
    74.                 } else {
    75.                     // not the same type, split
    76.                     Split();
    77.                     // tell the other one to split
    78.                     other.Split();
    79.                 }
    80.             }
    81.         }
    82.     }
    83. }
    You may want to consider renaming the class "DestroyCircles". It's not very accurate to the purpose and function of the object.
     
    Last edited: Feb 18, 2016
  12. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hey Jeffrey
    This is amazing, thank you so much for your time helping me out.
    I used to do a lot of AS2 and 3, HTML, CSS and some php, but never really got into C# until now. your comments help me a lot and I'm starting to understand most of it :D

    As soon I have all of this done, I will make a tutorial on merging objects to help others out and upload it on youtube. Credits to you of course :)

    Now to the code:

    We are really getting somewhere, splitting works just fine. Although a little to well. I have assigned the code to all the balls and chose the right type, triple checked that. But the same color wont merge, it will just split apart instead of growing or both suddenly get deleted at the same time.

    The other problem is that if one gets destroyed it somehow blows up, expands on the X axys over the entire field for a 0.1second.
     
    Last edited: Feb 18, 2016
  13. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Haha, glad to see that it's functioning at least. Those sound like some interesting bugs! I was afraid of some weirdness with so many things interacting on collision.

    Well, lets confirm our assumptions with Debug.Log() calls. These will print to the Console. (Window -> Console)

    I also split the if(sameType && biggerOrSame), and nested the biggerOrSame part inside. I think that might fix at least one of the weird things happening.

    You might want to test this with just two at a time so it's clear what's happening.

    Code (CSharp):
    1. private void OnCollisionEnter2D(Collision2D collision) {
    2.         // if this object can interact
    3.         if(canInteract) {
    4.             // get the other object's collider script
    5.             DestroyCircles other = collision.gameObject.GetComponent<DestroyCircles>();
    6.             if(other != null) {
    7.                 // true if the types match
    8.                 bool sameType = type == other.type;
    9.                 // nice way to compare the sizes of each Vector3 scale
    10.                 bool biggerOrSame = transform.localScale.sqrMagnitude >= other.transform.localScale.sqrMagnitude;
    11.                 // if they're the same type
    12.                 // and this one is bigger or the same size
    13.                 if(sameType) {
    14.                     Debug.Log("Same type!");
    15.                     if(biggerOrSame) {
    16.                         Debug.Log("Bigger or the same size! Absorbing...");
    17.                         // absorb the other one
    18.                         AbsorbOther(other.gameObject);
    19.                     }
    20.                 } else {
    21.                     Debug.Log("Different type! Splitting...");
    22.                     // not the same type, split
    23.                     Split();
    24.                     // tell the other one to split
    25.                     other.Split();
    26.                 }
    27.             }
    28.         }
    29.     }
     
  14. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Now I have only 2 whites and 2 black balls

    Following: Splitting from black to white works fine now.
    But: when the two of the same color collide, they both get destroyed instead of merge... :S your code makes sence... thats why I can figure out whats wrong..

    ohh and also, ill need to makes them stop splitting at some point, game freezes when they split to atom size >.<
     
  15. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Okay, to stop them from splitting after a certain point, you'll need to create a Vector3 variable to represent the minimum scale a ball can be. Then in the Split() function, check if the ball's scale is greater than or equal to double the minimum scale. If it is, then it can split and half its scale. If it's not, then it should either split and become the minimum scale, or do nothing.

    For the two of the same both getting destroyed, that means that the AbsorbOther function isn't properly disabling the other one from executing it's own AbsorbOther function. So let's try something else. I have a feeling that it takes a frame for the object to be destroyed or disabled, so the other ball still can run its "AbsorbOther" function in the frame before it dies. So maybe the script of the other ball should be directly altered in the current frame.

    Try adding this line to the AbsorbOther function:
    Code (CSharp):
    1. other.GetComponent<DestroyCircles>().canInteract = false;
     
  16. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    I will focus on the Merge thing before making them stop splitting actually.

    Unfortunately the code doesnt make a difference, I've put it into the AbsorbOther function but it wont change its behaviour.

    By the way, I changed it from DestroyCircles to BehaviourBall - that should make more sence and yes, I have changed it everywhere.

    Would it actually be a possibility to spawn a new one and let both get destroyed?
    just a thought.
     
  17. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Yes, that is a definite possibility, but which one will do the spawning? Right now the problem is finding a way to allow just one of the colliding objects handle the collision, when they both have the same script.

    Another solution might be to create some kind of an interaction manager. On collision the ball will tell the interaction manager which two objects collided. The manager will handle the collision, and won't accept duplicates. Unfortunately I don't have enough time to write up a concept for you to build on. If you like this approach, feel free to take a shot coding it and I'll help you next chance I get.

    Without getting too technical, you could create an InteractionManager script, and just keep it on an empty gameobject in your scene. Then you can either drag and drop the Manager reference to the ball prefabs, or have the balls do one of the "Find" methods to get the manager on start. Then its just a matter of moving the OnCollision logic to a manager function, and calling the Manager's function from the ball, passing it the two colliding objects.

    There's tons of different ways to keep track of which balls have collided. One could be to keep a list of interaction objects. You define a class called "Interaction" or whatever you like, with a public list of gameobjects. Thats all.

    Then when a ball calls the Manager's "HandleCollision" function or whatever you call it, the manager will check to see if those two objects are contained in any of the interaction objects. If not, make a new interaction object and add the two balls to the list inside it, add it to the list of interaction objects, and handle the collision. Then maybe after a very short delay, clear the list of interaction objects.

    Hopefully that will give you some ideas for how to get started going that route.
     
  18. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Right
    Right, sorry for the late reply and thanks for those ideas! I was gone over the weekend but back on it now :)
    I just noticed that when pressing on play, I suddenly find clones in the hierarchy. I zoomed out and saw the balls being made but outside the container and really stretched for some reason.
    So after all I think it works, but the outcome is now what we want... any ideas why that is happening?
    It kinda difficult to explain, if you want I could send you the project so you can see it for your self.
     
  19. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Well, from your description I went back at the code I gave you, and it looks like in AbsorbOther(), it's only adding scale to the X axis. You should fix that to include the Y axis as well. I don't know why they would be spawned outside the container however. Try setting the position explicitly in the Split() function.

    Like this:
    Code (CSharp):
    1. // make new copy of this ball at the current position & default rotation
    2. DestroyCircles newBall = Instantiate(gameObject, transform.position, Quaternion.Identity).GetComponent<DestroyCircles>();
     
  20. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Jeffrey, you are a f*** genious!!!!
    Sorry, very happy righht now! it worked but I had to downscale from 0.1 to 0.001 to make it work, it went out fo the box because it was to big to fit in it ;)


    THanks again, ill keep on working on it and tell you if ill make a youtube Vid from it :)
     
  21. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Ok, it works really well now. now only thing I need to do is to set the minimum size at its done :D otherwise it freezes my unity again hehe.
     
    LiterallyJeff likes this.
  22. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hey Jeff, real quick to that Project.. I have on more and last request, if you can help me. I actually thought of making this a game, an evade game with a timer.
    So far I have made another ball with an easy player mouse control:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MouseMove2D : MonoBehaviour
    5. {
    6.  
    7.     public float Speed;
    8.  
    9.     void Update()
    10.     { Vector3 Target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    11.         Target.z = transform.position.z;
    12.  
    13.         transform.position = Vector3.MoveTowards(transform.position, Target, Speed * Time.deltaTime);
    14.    
    15.  
    16.     }
    17. }
    Now I want to make the game stop when a black or white ball hits the players ball, stopping the survival timer displayed on the right.
    any idea / hints?
     
  23. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    The simplest way I think would be to give your player's Ball a collider, and a script with an OnCollision method that checks for other Ball objects. The player's script would have a public reference to whatever is driving the survival timer, and will tell the timer to stop when another Ball is detected.
     
  24. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20

    done that, good idea Jeff:D

    Now I notice one more bug... how come all the random moving balls start randomly to the bottom or left axis, none go right... where is the mistake in the code?
     
  25. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    ohh and one more thing, how do I set the minimum size for a ball? no make it stop lag :S
     
  26. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Try starting your Ball object with a force like this:
    Code (CSharp):
    1. // random vector in any direction, normalized to a length of 1
    2. Vector2 randomDirection = Random.insideUnitCircle.normalized;
    3.  
    4. ballrigidbody.AddForce(randomDirection * speed, ForceMode2D.Impulse);
    To keep your Ball object from getting too small, you'll have to check the scale before splitting. So you'll need a "minScale" to represent the scale that a ball should never be less than.

    Maybe something like this:

    Code (CSharp):
    1. public Vector2 minScale = new Vector2(0.05f, 0.05f);
    2.  
    3. public void Split() {
    4.     // calculate the half-scale for comparison
    5.     Vector2 halfScale = transform.localScale * 0.5f;
    6.  
    7.     // if the ball's half-scale is greater than the minScale
    8.     if(halfScale.magnitude >= minScale.magnitude) {
    9.         // half the scale
    10.         transform.localScale = halfScale;
    11.         // make new copy of this ball
    12.         BehaviourBall newBall = Instantiate(gameObject).GetComponent<BehaviourBall>();
    13.         // cooldown for this object
    14.         StartInteractionTimer();
    15.         // cooldown for the new object
    16.         newBall.StartInteractionTimer();
    17.     }
    18. }
     
    Last edited: Mar 2, 2016
  27. Sir_NoFace

    Sir_NoFace

    Joined:
    Feb 16, 2016
    Posts:
    20
    Hey there, that helped, thanks a lot for everything :)
     
    LiterallyJeff likes this.