1

The beginning of the problem is standard: I'm new to C # and Unity (3 weeks). For practice, I set myself tasks and try to solve them. First, simulate a ball falling to the ground. I coped with it. Second: simulate the fall of several balls to the ground, including their collision with each other. And then I realized that I did not have enough knowledge to implement it. While the ball was alone, everything was clear. But when there are several "players" ... Problem: I have 1 script assigned to each of the balls. But each ball has its own environment, and they do not see each other. With the help of static variables, I managed to partially solve the problem, but not completely: for some reason, not all balls collide with each other, but only some. Guys, tell me how to properly organize their interaction? I mean, how to correctly receive and process data about all balls.

I have highlighted the code that is needed to implement collisions between balls:

// +++++++++++++++++++++++++
// +++++++++++++++++++++++++
   some code
// +++++++++++++++++++++++++
// +++++++++++++++++++++++++

Sorry for my google.translate and thanks for help!

using System;
using UnityEngine;


public class Movement : MonoBehaviour
{
    public Transform player;
    public Transform ground;

    public float mass;    

    // other Force and direction
    private float force = 3f;
    private float directionForceRad = 45 * (float)Math.PI / 180;
    private Vector2 acceleration;
    private Vector2 movementSpeed;
    private Vector2 delta;
    private Vector2 normal;

    // parameters of ground
    private float leftEdge;
    private float rightEdge;
    private float groundAngle;
    private float cosGroundAngle;
    private float sinGroundAngle;


    private Boolean IsGrounded()
    {
        bool isGrounded = false;
        float dX = cosGroundAngle * (ground.localScale.x / 2);
        float dY = sinGroundAngle * (ground.localScale.x / 2);
        Vector2 distance = new Vector2(dX, dY) + (Vector2)ground.position - (Vector2)player.position;
        Vector2 projectionDistance = Vector2.Dot(distance, normal) * normal;

        if (projectionDistance.magnitude - 0.1 <= player.localScale.y / 2)
        {
            isGrounded = true;
        }
        return isGrounded;
    }

   
    // +++++++++++++++++++++++++
    // +++++++++++++++++++++++++
    public class Ball
    {
        public string name;
        public Vector2 movement;
        public float mass;
        public float radius;
        public Vector2 position;

        public Ball(GameObject obj, Vector2 mov, float weight)
        {
            name     = obj.name;
            radius   = obj.transform.localScale.x / 2;
            position = obj.transform.position;
            movement = mov;
            mass     = weight;
        }

    }

    static Ball[] allBalls;

    static GameObject[] balloons;

    private void CollisionOfBalloons()
    {
        Vector2 first, second;
        for (int i = 0; i < allBalls.Length; i++)
        {            
            for (int j = i; j < allBalls.Length; j++)
            {
                if (i != j)
                {
                    if (Vector2.Distance(allBalls[i].position, allBalls[j].position) <= allBalls[i].radius + allBalls[j].radius)
                    {                                             
                        first = (allBalls[j].mass * allBalls[j].movement) / allBalls[i].mass;
                        second = (allBalls[i].mass * allBalls[i].movement) / allBalls[j].mass;
                        allBalls[i].movement = first;
                        allBalls[j].movement = second;                        
                        Debug.Log("allBalls[i] = " + allBalls[i]);
                        Debug.Log("allBalls[j] = " + allBalls[j]);                        
                    }
                }
            }           
        }
    }

    // +++++++++++++++++++++++++
    // +++++++++++++++++++++++++



    void Start()
    {
        Vector2 gravity = new Vector2(0, -10);

        groundAngle    = ground.transform.rotation.eulerAngles.z * (float)Math.PI / 180;
        cosGroundAngle = (float)Math.Cos(groundAngle);
        sinGroundAngle = (float)Math.Sin(groundAngle);
        leftEdge       = ground.position.x - (ground.localScale.x * cosGroundAngle) / 2;
        rightEdge      = ground.position.x + (ground.localScale.x * cosGroundAngle) / 2;
        normal         = new Vector2(1 * sinGroundAngle, -1 * cosGroundAngle);

        // Components before start
        acceleration = new Vector2(
            (force / mass) * (float)Math.Cos(directionForceRad), 
            (force / mass) * (float)Math.Sin(directionForceRad) + gravity.y
            );


        // +++++++++++++++++++++++++
        // +++++++++++++++++++++++++


        balloons = GameObject.FindGameObjectsWithTag("Player");
        allBalls = new Ball[balloons.Length];
        foreach (var item in balloons)
        {
            int ind = Array.IndexOf(balloons, item);
            allBalls[ind] = new Ball(balloons[ind], acceleration, mass);
        }

        // +++++++++++++++++++++++++
        // +++++++++++++++++++++++++
    }


    void FixedUpdate()
    {
        foreach (var item in allBalls)
        {
            if (item.name == player.name)            
                movementSpeed = item.movement;            
        }

        // speed and distance
        movementSpeed   += acceleration * Time.deltaTime;
        delta           = movementSpeed * Time.deltaTime;        
        player.position = (Vector2)player.position + delta;


        // +++++++++++++++++++++++++
        // +++++++++++++++++++++++++

        foreach (var item in allBalls)
        {
            if (item.name == player.name)
            {
                item.position = player.position;
                item.movement = movementSpeed;
            }
                                      
        }
      

        CollisionOfBalloons();

        // +++++++++++++++++++++++++
        // +++++++++++++++++++++++++
                 

        // check collision and change direction
        if ((player.position.x > (leftEdge - player.localScale.x / 2)) && 
            (player.position.x < (rightEdge + player.localScale.x / 2)) &&
            IsGrounded())
        {
            foreach (var item in allBalls)
            {
                if (item.name == player.name)
                {
                    item.movement -= 2 * (Vector2.Dot(movementSpeed, normal) * normal);
                }
            }            
        }
    }
}

how it looks in Unity

  • 1
    Just in general .. I see it's for practice and that's fine .. but why reimplement something that already exists? ^^ in this case you would probably rather rely on the Unity built-in or a third party [Physics](https://docs.unity3d.com/Manual/PhysicsSection.html) library where someone (a team!) already has put all their knowledge, maths and optimization in for several years and which is probably more than these few lines of code ;) – derHugo May 11 '21 at 17:20
  • If I understand you correctly, I think you should just use rigidbody? I am not that’s what you want, but you could just have a script attached to something you wouldn’t use, for example an empty game object. Then just use `Instantiate();` to duplicate an object. Just add a prefab, which would have rigidbody on it. Let me know if this isn’t what you want. – gbe May 11 '21 at 17:21
  • first @derHugo is right, this already exists in Unity. second, you can use the `GameObject.FindGameObjectsWithTag("ball");` to get all of your balls – amitklein May 11 '21 at 17:23
  • @amitklein not really since the balls are no components on GameObjects but a normal c# class ;) – derHugo May 11 '21 at 17:26
  • No guys, I know my problems can be easily solved with rigitbody and collider2D, but I fundamentally do it manually to get the basics right.) @amitklein yes i used this in code. – Максим Можейко May 11 '21 at 17:26
  • 1
    This is not the basics, I understand you want to do things by yourself and it's great (fyi you can also use raycast) But it's important to understand that this is not really something anyone uses – amitklein May 11 '21 at 17:30
  • @amitklein let's postpone the question "should it or not", because this is not a practical application, but pure theory. Let's just try to solve the problem that we have.) For example, solving this problem will help me understand how one script works for different objects. That's a lot, don't you agree? – Максим Можейко May 11 '21 at 17:33
  • I didn't meant to say it's in any way wrong, just help you understand the uses of this. and if you want you can use the `raycast` functions to help you with this – amitklein May 11 '21 at 17:36
  • @amitklein No, it won't help. Because I have no problem detecting a collision. The problem is in handling them. For each ball I have my own script, and not one common script for everyone. So, for example, I cannot get all the data I want in one of them. I don't know how to explain it to you differently, I'm sorry. – Максим Можейко May 11 '21 at 17:44
  • If I understand you correctly the raycast `hit.collider.gameObject.name` should work. It gives you the object you hit with the raycast – amitklein May 11 '21 at 20:16
  • OP is just saying he doesn't understand how to handle "globals" for want of a better term in a game engine. @МаксимМожейко here's a full explanation https://stackoverflow.com/a/35891919/294884 have a "central" MonBehavior (perhaps called "Physics") and build your engine in there. the various balls would basically be members of a list or array of some type. Good luck. – Fattie May 11 '21 at 20:24
  • @fattie so far this is the best post on this thread. At least he describes my problem correctly. I will definitely read that material. Thanks.) Can someone else advise? – Максим Можейко May 11 '21 at 20:39

2 Answers2

0

If (incredibly) you want to build your own physics engine, this is the basic text book all physics game programmers know by heart.

https://www.amazon.com/Mathematics-Programming-Computer-Graphics-Third/dp/1435458869/

enter image description here

Before even getting started you'll need flawless abilities with all major languages, and then move on to that book.

halfer
  • 19,824
  • 17
  • 99
  • 186
Fattie
  • 27,874
  • 70
  • 431
  • 719
0

There's a common design pattern of a "Manager" class that I would employ here. I would start there, I would separate the logic (the physics/collisions methods) and your data (RigidBodies with mass, colliders, velocities etc.)

class PhysicsManager : MonoBehavior{
    public PhysicsBodies PhysicsBody []; //you can add to here in the inspector
    public void Simulate(){
      //todo: for all bodies... simulate collisions and physics
    }

}

You currently seem to be interested more in engine programming topics, in general I would be careful to model your code in overly Object-Oriented manner (where data and logic are linked) as it tends to lead to architectural and performance problems in this domain.

class PhysicsBody : MonoBehavior {
   public float Mass;
   public Vector3 Velocity;
   //etc
}
Charly
  • 450
  • 2
  • 13
  • Actually I just saw that you weren't using MonoBehaviors, this is really good for your goals! Really you're just using Unity as a rendering engine and for it's update loop. I really would encourage you to continue to seperate your code from Unity's and only link them together where you need to (in your case, for the update loop, and right before rendering to move the transforms to the correct locations to match your physics sim). – Charly May 11 '21 at 23:54