BoundingSphere Update - c#

Hallo I'm writting little RPG about Ants in XNA 4.0. I have made LoadModel class to fbx model load, and to create bounding spheres. I have one general bounding sphere at the model created by merging model mesh. Now i created model with additional sphere which represent my bounding sphere in game. I just checking if the Mesh name is "BoundingSphere" and when it is I'm adding mesh.BoundingSphere to my array of bs.
Now i dont now how to update those bs...
My code attempts:
private void buildBoundingSphere()
{
BoundingSphere sphere = new BoundingSphere(Vector3.Zero, 0);
List<BoundingSphere> spheres = new List<BoundingSphere>();
foreach (ModelMesh mesh in Model.Meshes)
{
if (mesh.Name.Contains("BoundingSphere") )
{
BoundingSphere transformed = mesh.BoundingSphere.Transform(modelTransforms[mesh.ParentBone.Index])
spheres.Add(transformed);
sphere = BoundingSphere.CreateMerged(sphere, transformed);
}
}
this.boundingSphere = sphere;
this.spheres = spheres.ToArray();
}
Now the BoundingSphere array get/set:
public BoundingSphere[] spheres
{
get
{
// No need for rotation, as this is a sphere
List<BoundingSphere> spheres = new List<BoundingSphere>();
foreach (ModelMesh mesh in Model.Meshes)
{
Matrix worldTransform = Matrix.CreateScale(Scale)* Matrix.CreateTranslation(Position);
if (mesh.Name.Contains("BoundingSphere")) {
BoundingSphere transformed = mesh.BoundingSphere.Transform(worldTransform);
spheres.Add(transformed);
}
}
return spheres.ToArray();
}
set{}
}
I have 2 spheres in model, and both of them are in the same place.
Thank you for any hint.

For one thing your setter is empty. If you're going for an auto-generated setter, use set; instead of set{}. So it seems everything you do in the buildBoundingSphere() is lost. Verify that first.
Another minor issue is that your bounding sphere accumulator starts as a point at the origin. This assumes the origin will be inside the final sphere, which is not true in general. Really you should somehow only generate the starting sphere once you know the first sphere.
I can't tell anything more from this snippet. If you're going to transform all the spheres in the getter, is it by the object's current transform? Scale and Position don't seem dependent on the mesh it's transforming the sphere of, so that construction of the worldTransform matrix could be moved outside the loop.

Related

OverlapBox returns null almost every time

I've tried to make a script to spawn objects at a random position without them colliding with one another. It doesn't work properly as the OverlapBox returns almost every time null even when it touches a square.
Here is the script:
var quadBoundaries = quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);
foreach (var square in squaresToSpawn)
{
_isOverlapping = true;
while (_isOverlapping)
{
_spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f),
Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));
_collisionWithSquare = Physics2D.OverlapBox(_spawnPoint, squareRadius,
0, LayerMask.GetMask("Square Layer"));
if (_collisionWithSquare is null)
{
square.transform.position = _spawnPoint;
_isOverlapping = false;
}
}
}
The quadBoundaries are the boundaries of a quad I placed so the squares will randomly spawn in a limited space.
My understanding is that I am generating a random point in the quad boundaries and then I check if on that point a square of scale (1,1) will fit without touching any other thing that has a collider and is on the square layer. if it touches then I generate a new point until the collision is null so I can place the square at the designated position.
But a bunch of things that I don't understand are happening.
First, the squares are touching each other. Second, just a few specific squares are registering a collision but even those are being touched by other squares. Third, when I scale up the square radius (for example 10,10) I get a big split between the squares (shown in the picture bellow).
I must add that all squares have a collider, are all on the square layer and the quad is on a different layer.
Can anybody explain to me what I'm not getting here? Thanks a lot!
Before the answer I'd like to say, that such algorithm of spawning is very dangerous, because you can enteran infinity loop, when there will no be place for new square. Minimum, that you can do to make this code more safe is to add a retries count for finding a place to spawn. But I will leave it on your conscience.
To make this algorithm work, you should understand that all physics in Unity are updated in fixed update. So all operations, that you do with Phisycs or Phisics2D are working with the state of objects, that was performed in the last Pyhsics update. When you change the position of the object, physics core don't capture this changes instantly. As a workaround you can spawn each object in the fixed update separately. Like this:
public class Spawner : MonoBehaviour
{
[SerializeField] private GameObject[] _squaresToSpawn;
[SerializeField] private GameObject _quad;
// Start is called before the first frame update
void Start()
{
StartCoroutine(Spawn());
}
private IEnumerator Spawn()
{
var quadBoundaries = _quad.GetComponent<MeshCollider>().bounds;
var squareRadius = new Vector2(1, 1);
Vector2 spawnPoint;
foreach (var square in _squaresToSpawn)
{
yield return new WaitForFixedUpdate();
var isOverlapping = true;
var retriesCount = 10;
while (isOverlapping && retriesCount > 0)
{
spawnPoint = new Vector2(Random.Range(quadBoundaries.min.x + 1.5f, quadBoundaries.max.x - 1.5f),
Random.Range(quadBoundaries.min.y + 1.5f, quadBoundaries.max.y - 1.5f));
var hit = Physics2D.OverlapBox(spawnPoint, squareRadius,
0, LayerMask.GetMask("Square"));
if (hit is null)
{
square.transform.position = spawnPoint;
isOverlapping = false;
}
else
{
retriesCount--;
if (retriesCount == 0)
{
Debug.LogError("Can't find place to spawn the object!!");
}
}
}
}
}
}
But such code will have effect of continious spawning:
To make objects spawning properly within one frame. You should manualy store all spawned objects bounding boxes inside your code, and manualy check if your new spawn bounding box collide with previously spawned objects.

Split a bullet in 3 different directions using Quaternion.AngleAxis

I'm trying to assign a skill to a projectile that, when used, divides the project into 3 (the original projectile and 2 more new ones).
However, when I instantiate these two clones, they keep following the same trajectory. The idea would be for them to take this route:
The green dotted curve indicating the motion of the original bullet, the blue vector indicating the instantaneous velocity of the original bullet at time of special activation, the red vectors indicating the two velocity vectors belonging to each of the newly spawned bullets, and the green angle indicating the direction of the new bullet relative to the original velocity direction
But at the moment, they spawn and continue following the same trajectory as the original. The sprites even rotate to the right angle, but that doesn't seem to make much difference in how the physics is applied.
Does anyone know how I can solve this?
This is my code so far
Ability Script:
public class AirSpecialSplit : MonoBehaviour, IAirSpecial
{
public float SplitAngleInDegrees = 10;
GameObject bird_down;
GameObject bird_up;
public void ExecuteAirSpecial()
{
{
//hold the velocity of the original bird
Vector2 original_velocity = this.gameObject.GetComponent<Rigidbody2D>().velocity;
//clone two new birds
bird_down = Birb.MakeBirbCopy(this.gameObject);
bird_up = Birb.MakeBirbCopy(this.gameObject);
//apply the angle to the clones
bird_down.transform.rotation = Quaternion.AngleAxis(-SplitAngleInDegrees, Vector2.up);
bird_up.transform.rotation = Quaternion.AngleAxis(SplitAngleInDegrees, Vector2.up);
//get the rigidboy from the clones
Rigidbody2D rb_bird_down = bird_down.GetComponent<Rigidbody2D>();
Rigidbody2D rb_bird_up = bird_up.GetComponent<Rigidbody2D>();
rb_bird_down.simulated = true;
rb_bird_up.simulated = true;
rb_bird_down.velocity = new Vector2(original_velocity.x, original_velocity.y);
rb_bird_up.velocity = new Vector2(original_velocity.x, original_velocity.y);
}
}
}
Well you apply the same velocity to both so of course they will move in the same direction. The velocity is in world space!
You probably wanted to rather add the rotation like e.g.
rb_bird_down.velocity = bird_down.transform.forward * original_velocity.magnitude;
And before that you probably should take the current bullet rotation into account like
bird_down.tranform.rotation = transform.rotstion * Quaternion.Euler(0,0, -SplitAngleInDegrees);

Unity3d - Get GameObject height for a rotating object

I want to get a GameObject's height. I have tried with:
this.GetComponent<MeshRenderer>().bounds.size.y
But the problem with bounds is that it works with static objects only. When you have a moving object and if the rotation of the object is not perfectly aligned, the bounds (height) is not accurate anymore because it returns the height of the bounds that is a square and if you tilt an object like a plate you get the bounds height which is not accurate to the height of the object.
It is an Axis-Aligned Bounding Box (also known as an "AABB").
Please check the image I attached, there you can see the problem with moving objects and if you rotate them how the height is not accurate anymore.
Did anyone else have this kind of problem?
Any advice on how to get the object's height accurately?
One option is to use the Game Object's Collider.
You can relatively easily find the height and width of a collider so this is one way you can measure the dimensions of a game object. The collider, for example a box collider on the game object, will have to fully encompasses the object. You can do this be resizing the collider until it snugly wraps around your game object. Then when you want to find a certain dimension of the object instead find that dimension on the collider and multiply by the Game Object's transform.scale.
Here are some examples I have tested.
Example 1:
CapsuleCollider m_Collider = GetComponent<CapsuleCollider>();
var height = m_Collider.height * transform.localScale.y;
https://docs.unity3d.com/ScriptReference/CapsuleCollider-height.html
or
Example 2:
BoxCollider m_Collider = GetComponent<BoxCollider>();
var height = m_Collider.size.y * transform.localScale.y;
var width = m_Collider.size.x * transform.localScale.x;
var breadth = m_Collider.size.z * transform.localScale.z;
https://docs.unity3d.com/ScriptReference/BoxCollider-size.html
Hopefully one of these will work for you.
If not, then a second inconvenient way you can find the height is to create 2 proxy objects as children at the top and bottom of your object. Afterwards find the scalar distance between them.
Solution 1: Use Mesh.bounds
You can get the height using Mesh.bounds. Unlike MeshRenderer.bounds (or Renderer.bounds), this is the axis-aligned bounding box of the mesh in its local space (i.e. not affected by the transform).
You still need to account for the scale of the GameObject using transform.lossyScale, as follows:
public class Example : MonoBehaviour
{
private Mesh _mesh;
private void Awake()
{
_mesh = GetComponent<MeshFilter>().mesh;
}
private void Update()
{
float height = _mesh.bounds.size.y * transform.lossyScale.y;
Debug.Log(height);
}
}
(Note when using transform.lossyScale, the value can be slightly inaccurate. This is something to note if you need extreme accuracy. See the documentation on transform.lossyScale:)
Please note that if you have a parent transform with scale and a child
that is arbitrarily rotated, the scale will be skewed. Thus scale can
not be represented correctly in a 3 component vector but only a 3x3
matrix. Such a representation is quite inconvenient to work with
however. lossyScale is a convenience property that attempts to match
the actual world scale as much as it can. If your objects are not
skewed the value will be completely correct and most likely the value
will not be very different if it contains skew too.
Solution 2 (Workaround): Cache the unrotated MeshRenderer bounds
Alternatively, if in your context you are able to instantiate the GameObject without a rotation and you only need to rotate the GameObject after initialization, one workaround solution is to cache the bounds from MeshRenderer in the Awake method, as follows:
public class Example : MonoBehaviour
{
private Bounds _initialUnrotatedBounds;
private void Awake()
{
InitializeUnrotatedBounds();
}
private void InitializeUnrotatedBounds()
{
Assert.IsTrue(transform.rotation == Quaternion.identity);
_initialUnrotatedBounds = GetComponent<MeshRenderer>().bounds;
}
private void Update()
{
// Use the cached bounds. Now, even for moving objects,
// it doesn't matter if the rotation changes
float height = _initialUnrotatedBounds.size.y;
Debug.Log(height);
}
}

Only first NavMeshAgent can be placed

I am having an issue where only the first NavMeshAgent can be created.
First of all, I selected all mesh renderers for the NavMesh bake objects:
I fine-tuned the bake a bit so it had just the right amount of detail. The black gaps around the various objects would be larger if I used a larger radius in the baked agent options:
I have two spawners placed on either side of the back ramp (the one with the white sphere in it). These are invisible but in a script, NavMeshObjects are placed onto these locations on an interval and have their destinations set to one of the other gameobjects.
The first one works fine, but none of the other 17 NavMeshObjects in the spawner queue can have their destinations set.
public class MinionSpawner : MonoBehaviour {
public void spawn (GameObject minion, GameObject target_position_obj) {
// Find the target position from the passed gameobject
MinionTargetPosition target_position = target_position_obj.GetComponent<MinionTargetPosition>();
if (!target_position) { throw new System.Exception("no valid target position given"); }
// Instantiate the given minion at the spawner's origin point
GameObject new_minion = Object.Instantiate(minion, transform.position, new Quaternion(0,0,0,0));
new_minion.SetActive(true);
// Mark the minion at it's target position, for lookup upon collision or elsewhere
MinionAttributes minion_attrs = new_minion.GetComponentInChildren<MinionAttributes>(true);
if (!minion_attrs) { throw new System.Exception("object passed as minion does not have MinionAttributes"); }
minion_attrs.target_position = target_position;
// Configure a NavMeshAgent to navigate the minion from origin to target position.
NavMeshAgent nav_mesh_agent = minion_attrs.gameObject.GetComponent<NavMeshAgent>();
if (!nav_mesh_agent) { throw new System.Exception("minion doesn't have a nav mesh agent"); };
nav_mesh_agent.Warp(transform.position);
// ================================================================
// THIS LINE FAILS:
nav_mesh_agent.destination = target_position.transform.position;
// ================================================================
// mark the minion's position as occupied in the turret's dictionary
Turret turret = target_position.turret;
turret.minion_slots[target_position] = true;
// set the minion's parent to the minion spawner, for organization's sake.
minion.transform.parent = transform;
}
}
I'm getting the error "SetDestination" can only be called on an active agent that has been placed on a NavMesh. and I know there are questions out there about this. But I've tried really everything I've found suggested, including baking the NavMesh, setting the NavMeshAgent's initial position with Warp, and making sure the points are on the mesh.
The issue was something unrelated to the NavMeshagent. It was this line:
minion.transform.parent = transform;
I needed to change it to:
new_minion.transform.parent = transform;
Basically minion is the object that gets cloned and new_minion is the clone. So if I set the parent of minion, I am messing it up for future clones, I guess.

How do I change vertices of a tree from Terrain in Unity3d with script?

I have placed some trees onto the terrain via editor. I have an object which is passing throuh trees and when this object collides with certain tree I want to change its vertices.
void OnTriggerEnter (Collider col) {
DestroyableTree tree = col.gameObject.GetComponent<DestroyableTree>();
if (tree == null)
return;
Mesh mesh = tree.GetComponent<MeshFilter>().mesh;
Vector3[] vertices = mesh.vertices;
for (var i = 0; i < 100; i++){ // 100 just random number, as well as 50 bellow
vertices[i].x = 50;
vertices[i].z = 50;
}
mesh.vertices = vertices;
mesh.RecalculateBounds ();
mesh.RecalculateNormals ();
tree.GetComponent<MeshFilter> ().mesh = mesh;
//tree.UpdadeMesh(tree, mesh);
//tree.Delete();
}
DestroyableTree is from http://rene.klacan.sk/unity3d/games/2014/10/28/destroyable-terrain-trees-in-unity/
When uncommented tree.Delete() lets me be sure that I was trying to edit correct tree.
When I debug I can see that vertices of mesh have changed but why they won't update during runtime of game scene?
EDIT: it is probably because I am accesing capsule component's mesh but not of the tree itself. Still how do I access mesh of trees on the terrain (not necessarily with the code displayed above)?
Using code provided in the link above and having it modified a bit worked. Although it didn't solve the very root issue why I had to ask this question in the first place :(
So here it is how I did it, simple: in the class DestroyableTree that René Klačan wrote I added additional field public TreeInstance treeInstance; which gets initialized after marked statement (in the picture bellow) by adding tree.treeInstance = treeInstance;:
After that I had to reach mesh of the tree using tree.treeInstance.GetComponent<MeshFilter>().mesh; in my code at the beggining of the question. I hope someone will find this useful.

Categories

Resources