Unity3d maintaining a minimum distance between multiple GameObjects c# - c#

I have multiple enemies that move toward the player. I am trying to stop them from merging into each other, i.e maintain some type of minimum distance between each other. What I'm trying is this (from unity3d forums):
enemy1.transform.position = (enemy1.transform.position -
enemy2.transform.position).normalized * distance + enemy2.transform.position;
However, when I have >= 3 enemies they still seem to bunch up even when I apply this to every enemy combination.
I need a better method as this one does not work and does not scale.

What you do around your enemy prefab is place a quad box which in effect is a trigger element, then on you script for the enemy you set that if another enemy (using tag types, I imagine) comes within touching of the quad box then to run appropriate code to stop the enemy getting closer in and to prevent the enemy actually overlapping with this trigger box.
Your code you example in your question looks like it is referencing the enemy units specifically rather than as instance entitites which is very probably a bad way of coding, as you're finding, it's very inflexible dealing with not-specifically-named reference entities.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
private Transform otherEntity;
public float distance = 50.0f;
private Transform thisEntity;
void OnTriggerEnter(Collider other)
{
otherEntity = other.GetComponent<Transform>();
thisEntity = GetComponent<Transform>();
thisEntity.position = (thisEntity.position - otherEntity.position).normalized * distance + otherEntity.position;
}
}
I have not tested the above code but using the code you reference the values for the transform are loaded from the Game Component for the referenced entities, as this has changed ALOT in Unity since release of version 5.1 and your source for your code was an answer given in 2012!!
thisEntity is the object that the script is attached to, typically one of the enemy. The otherEntity is the object that comes within the quad trigger (please remember the quad needs to be referenced as a trigger element.
The values of the Entity variables need to be set once the trigger is entered, so that it always effects the correct other element.
(I may be totally wrong with my methodology but I hope this helps.)

Related

Unity Physics.Raycast with LayerMask does not detect object on layer. Used bitshifting, tried inverting layer, still nothing works

novice to intermediate Unity developer here. I've been hitting a pretty significant roadblock the past ~2 days concerning the raycast detection of objects with specific layers. I've been researching this issue quite a lot, and all the solutions I've found don't seem to reflect the strange issue I'm facing.
Basically, the problem follows this sequence of events:
My player character has a vision cone shaped trigger mesh called 'InSightBox' which detects all objects with the tag 'Mob' and adds them to a List of colliders called 'MobsInRange'.
public List<Collider> mobsInRange;
public List<Collider> GetColliders()
{
return mobsInRange;
}
// Start is called before the first frame update
void Start()
{
mobsInRange = new List<Collider>();
}
// Update is called once per frame
void Update()
{
}
//add enemy with tag 'mob' to list
private void OnTriggerEnter(Collider other)
{
if(!mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Add(other);
}
}
//remove enemy with tag 'mob' to list
private void OnTriggerExit(Collider other)
{
if (mobsInRange.Contains(other) && other.tag == "Mob")
{
mobsInRange.Remove(other);
}
}
This list is then fed up to the root/parent player game object containing everything relating to the player.
public Transform closestMob;
public List<Collider> mobs;
public Transform GetClosestEnemy()
{
Transform tMin = null;
float minDist = Mathf.Infinity;
Vector3 currentPos = transform.position;
foreach(Collider trans in mobs)
{
//find enemy with closest distance and set tMin to it. Method returns tMin
float dist = Vector3.Distance(trans.transform.position, currentPos);
if(dist < minDist)
{
tMin = trans.transform;
minDist = dist;
}
}
//Debug.Log(tMin);
return tMin;
}
The player then uses a 'Look at' method to find the closest of all 'mobs' to the player. The player will set their forward transform to look at the closest mob.
Problem Step ---> 4) When the player raises their gun and attempts to shoot the closest enemy, a ray is cast with a layermask that looks only for objects on the layer 'Enemy', the 8th layer. When the ray detects the enemy, the enemy script should fire its 'TakeDamage' method which decreases the 'curHealth' variable by 8. Only problem is, the cast doesn't seem to detect the enemy object on the 'Enemy' layer.
LayerMask layerMask = 1 << 8;
void Fire()
{
//play the audio of the gunshot
StartCoroutine("SetPlaying");
RaycastHit hit;
//cast a ray from the player forward and check if the hit object is on layer 'Enemy'
if (Physics.Raycast(transform.position, transform.forward, out hit, Mathf.Infinity, layerMask))
{
//if hit object is an enemy, set its 'gotShot' bool to true
print("hit enemy");
closestMob.GetComponent<EnemyBase>().gotShot = true;
}
//play gunshot sound and stop player from turning
source.clip = fireSound;
source.PlayOneShot(fireSound, gunshotVolumeScale);
turnSpeed = 0;
}
I'll also note that all the solutions I've seen to this issue are not working for me. I declared an int variable called 'layerMask' and initialized it in Awake() by bit shifting layer 8 into it (i.e. int layerMask = 1 << 8), but it still isn't detecting it. The enemy contains all that I belive it should need for this to work, including a rigidbody, a capsule collider, the associated scripts, as well as being on the 'Enemy' layer.
This is where it gets weird (at least to my knowledge), when I invert the mask in the cast (~layerMask), it does exactly what I'd expect, and begins firing the code within the raycasts if statement when the player 'shoots' anything that doesn't have the 'Enemy' layer.
Any help would be suuuper appreciated as I'm getting to the point of slamming my face into the desk :/
Side Note: I'm getting to the point where I may just attach a 'fire range' cube trigger to my player and enabled it when the Fire() event is triggered, then have that check for game objects with the tag 'mob' as that kind of detection works most consistent to me.
First, be sure that you didn't confuse layers with tags. They are different.
Second, get rid of any implicit actions and references, i.e. don't use bitshifting, layer indices, or any non-straightforward reference. Instead, create something like this:
[SerializedField] private LayerMask _layerMask;
Use inspector to assign needed layer(s).
This way you will explicitly see, which layer you are using. This is useful not only for you, but for you in future, when you forget layers' indices. Also, for anyone who aren't familiar with the project.
Third is for debug. Be sure that your raycasting works as intended at other aspects:
Try remove layerMask and see if the ray goes where you want it to
Use custom gizmos to check if you cast the ray in a right direction
Try using RaycastAll. Maybe some objects catch (block) your ray earlier than you think
Looks like the issue was that I had my 'Enemy' prefab which contained the necessary components (RigidBody, Collider, scripts, NavmeshAgent) nested inside an empty game object. I thought at first to make something a prefab you needed to have it inside an Empty. I see now that is rather redundant and not necessary (at least in my case).
Physics.RaycastAll solved this issue as it no longer got 'halted' by the parent empty's collider and also hit the child.
I actually got it working just using a regular Physics.Raycast by rebuilding the Enemy prefab as just a single Capsule object (which I'll replace with character meshes later on).
Side Note
Before I got this new method working, I also used a different one that achieves the same goal in a pretty lightweight manner.
I added a long and thin box trigger to the front of my player so that it has enough distance to collide with any enemies within the shooting range.
I then enable the trigger any time the player enters their 'Aiming' state. If the trigger collides with any mesh that has the tag "Mob', it sets a bool on the player that indicates if the enemy is in range.
Then if the enemy is in range && the player enters the 'Firing Gun' state, a method in the Enemy Base script is fired that decrements the enemy health by a publicly decided variable called damagetaken.
Both the Raycast method and the box trigger method work equally well for me, but the box trigger one just took less time to figure out and gave me less headache haha.
Hope this helps anyone else in a bind!!!!!

Unity recompile serialized script

I'm rather new to Unity and trying to get started with a simple scene consisting only of an FPS Controller and a few obstacles.
As the controller I'm using the one from the Unity Standard Assets and everything worked fine.
Upon adding a Character Mesh however I noticed that I had to setup camera clamping. The controller script accesses the serialized "MouseLook" script in which I have to edit a few variables to fit my needs.
Since it's serialized however I can't simply change the variables, save the code and get the changes.
Since I'm not planning on changing the fact that the script is serialized I just want to update my changed variables.
I hope this was understandable. Thanks in advance!
Hello and welcome #Corneey,
For starters, I have no idea how to recompile that script or change any values (except for some input fields it might have?), but there's a workaround:
public class LookAtMouse : MonoBehaviour
{
//The object to rotate with/look at the mouse
public Transform target;
//Update is called once per frame
public void Update()
{
//Get mouse position in 3D space
Plane target = new Plane(Vector3.up, 0);
Ray r = Camera.mainCamera.ScreenPointToRay(Input.mousePosition);
//On the hit, rotate the target to the "impact point"
if(target.Raycast(r, out float distance)
Vector3 hitPoint = r.GetPoint(distance);
}
}

Unity 3D - Moving Object & Player with Push/Pull || Applying Opposing Force

so I've been struggling with a problem for a while, not sure how to approach it.
I'm trying to simulate a magic system similar to The Force used in Star Wars in Unity, and have ran into some troubles. I'm trying to simulate pushing and pulling on objects appropriately. This means that when the player pushes an object, both the player and the object get a force applied to them. If the object is light enough, the player shouldn't be affected in any noticeable way, and the object should be pushed away. If their masses are similar, both should be pushed away at some rate, and if the object is heaver, the player should be mainly pushed away. The same goes with pulling. The problem is when objects are anchored against something. If a player pushes a light object into a wall, the player should start receiving enough force to start pushing them backwards, since the combined mass of the wall and the object are pushing the player back.
Currently, I'm able to do the first part with the following code when a key is pressed:
Note that the force is determined based off the distance from the player to the object and the mass of them.
// dir: push or pull (-1 or 1)
Vector3 directionToPlayer = (dir * (player.transform.position - transform.position)).normalized;
rb.AddRelativeForce(directionToPlayer * f * Time.deltaTime);
player.GetComponent<Rigidbody>().AddRelativeForce(-directionToPlayer * f * Time.deltaTime);
As it is, the code works fine when the objects are just by themselves just by relying on Unity's Physics system. However, it does NOT work when an object is being pushed against a wall, or something really heavy. This is pretty clear as I'm just applying the same force to both the object and the player, and letting Unity decide whether to move the entity based off its rigidbody mass.
My question is, how can I detect if an object is anchored, and if it is combine the mass of the object and the object which it is anchored to, and apply that force to the player? I was thinking how to do this, and I know that I need to figure out the direction of which the force is being applied from, and then determine whether the object that's being pushed or pulled is colliding with anything in that direction, and if so apply a force to the player based off the combined masses. This gets a little tricky however when pushing an object at an angle that's against a wall. For example consider the following scenario:
There is both a horizontal and a vertical component of the force being applied on the cube, and so that should be considered when the cube+wall combined object pushes back on the player. How could this be accomplished or approached? I'm really struggling coming up with how to do this without hard coding direcitons.
Any help is appreciated, and I'll gladly clarify if anything is unclear.
NOTE The system is really based from a book series called Mistborn, but the concept is really close to The Force in Star Wars, and so I'll run with that as more people are familiar with it
This is not a complete solution, but you can use my idea below to acquire the nearby objects and then apply force to your objects and player in accordance with that.
The script should be placed on the object you are pushing or at least use a sphere collider from the object you are pushing.
public List<string> _ValidTargetTags = new List<string> { "Wall" };
public void CheckNearbyArea()
{
//Each of the nearby items will be checked to see if they are a wall or other valid target.
Collider[] ObjectsStruck = ScanForItems(GetComponent<SphereCollider>());
foreach (var o in ObjectsStruck)
{
if (_ValidTargetTags.Contains(o.gameObject.tag))
{
//If wall or other valid target
//Check where wall is placed
//Act according to wall position.
}
}
}
///The method below should find a list of all items inside a spherecollider of the object you are pushing.
Collider[] ScanForItems(SphereCollider sphereCollider)
{
Vector3 center = sphereCollider.transform.position + sphereCollider.center;
float radius = sphereCollider.radius;
Collider[] allOverlappingColliders = Physics.OverlapSphere(center, radius);
return allOverlappingColliders;
}
With this solution you will be able to check the players location relative to any nearby walls. This you can use to calculate what his direction is compared to the wall, relative to the object he is pushing.

Unity 3D colision of objects

I have a 3D game that is generated randomly when you hit play. The thing is that objects don't generate with collision and characters start moving through walls. What is the necessary code for an object to be generated with collision in C# ?
Ok if I have read your question correctly, you are auto generating random meshes for you game but they do not yet have colliders on them. To generate a collider for an object in c# see the following example:
using UnityEngine;
using System.Collections;
public class AddComponentExample : MonoBehaviour
{
void Start( )
{
SphereCollider sc = gameObject.AddComponent<SphereCollider> as SphereCollider;
}
}
You need to add a collider component to the gameobject you are generating. The one you choose depends on the mesh you created, so if you are generating cube meshes choose a box collider.
If you are creating your own meshes instead of primitives that are more complex then you can apply a "MeshCollider". But be warned if you are creating your own mesh at run time you may get some unusual artefacts.
Hope that helps

Instantiate and apply new PhysicsMaterial2D in script (Unity)

I'm working on a Unity game. In it, I want to be able to give different entities different degrees of friction without creating multiple materials in the editor. I'm currently trying to do this by defining a PhysicsMaterial2D as a member variable and instantiating it in the Start method. The material's friction is then applied based on a public member variable. I then set the actor's collider2D's sharedMaterial.
public class ActorUpdateScript : MonoBehaviour {
public float friction;
PhysicsMaterial2D actorMaterial;
void Start () {
actorMaterial = new PhysicsMaterial2D();
actorMaterial.friction = friction;
collider2D.sharedMaterial = actorMaterial;
}
}
I can verify that collider2D.sharedMaterial is indeed receiving the new material, as I can print it's friction and it gives me the expected result. However, no matter what number I give it's friction, it always behaves like the default material. (0.4 friction I believe). The collision boxes he's walking on all have a material with a friction of 1.
What am I doing wrong? Does anyone have any suggestions on how to make unity pay attention to the material I've applied?
Also, before you tell me, I know I can have different materials (high friction, medium friction, low friction, etc) that I apply to different entities. But I'm planning on eventually adding super basic modding support, so the friction for a given entity will be set with a JSON file.
The problem is probably that you have a physics material on both your character and the floor and the combine method is to take the smaller of the two, you will always get the lower value.
To fix this you either remove the physics material from the character, set it high enough it always gets overwritten or change it depending on what you are standing on to alway get the correct behavior.
void Start () {
actorMaterial = new PhysicsMaterial2D();
collider2D.sharedMaterial = actorMaterial;
collider2D.sharedMaterial.friction = friction;
}
Assign new material to collider.sharedMaterial first.
And change material value.
It works for me.

Categories

Resources