i am trying to make some types of pickups spawn inside a given area, although some usually get stuck within the walls, how would i fix this?
Code in question for moving objects
for (int x = 0; x < garbage.Length; x++)
{
if (x < 5)
{
garbage[x].transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
}
}
Fixed it using Physics.OverlapSphere. Thanks.
You could have a while loop inside your if statement, so it would be like
int attempts = 0;
while(garbage[x].transform.position == /*[the range of coordinates for the wall]*/ || attempts = 0)
{
garbage[x].transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
attempts += 1;
}
You can try OnCollisionStay to tackle this with collisions. OnCollisionStay can be very cpu heavy if not used carefully, so you may want to think of a better way if you can.
You will have to create a new script using the following code, which you will attach to your power-up prefab.
bool keepChecking = true;
void OnCollisionStay(Collision collision)
{
if(keepChecking)
{
if(collision.gameobject.tag == "Wall")
{
collision.gameobject.transform.position = new Vector3(Random.Range(-33.0f, 30.0f), 2.35f, Random.Range(30.0f, -35.0f));
}
else
{
keepChecking = false;
}
}
}
https://docs.unity3d.com/ScriptReference/Collider.OnCollisionStay.html
Read that link and make sure your objects have all the requirements. Your wall and Power-Up should have colliders, and at least one of these two should have a rigid body. None of these objects should be kinematic.
Let me know if this works for you.
Related
First of all, i created a GIF to show what is currently happen.
GIF with my current problem
and
Awhat I want
I have a List of GameObject which add the bodyParts temp and Instantiate it in the correct time and position.
Now this is working like expected, but i want this new bodyParts below another object instead of above.
As you can see the Head is "under" the new body parts, but it should always on Top and every new part should spawn under the next. (only should looks like! I dont want to change the Z position.)
i tried :
bodyParts.transform.SetAsFirstSibling();
to change the Hierarchy, but this do nothing. I also can drag and drop the Clones to a other position in Hierarchy but they just stay at the same position (above another).
Is this possible and what should i have to do?
Here some of my Code which makes the process:
private void CreateBodyParts()
{
if (snakeBody.Count == 0)
{
GameObject temp1 = Instantiate(bodyParts[0], transform.position, transform.rotation, transform);
if (!temp1.GetComponent<MarkerManager>())
temp1.AddComponent<MarkerManager>();
if (!temp1.GetComponent<Rigidbody2D>())
{
temp1.AddComponent<Rigidbody2D>();
temp1.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp1);
bodyParts.RemoveAt(0);
}
MarkerManager markM = snakeBody[snakeBody.Count - 1].GetComponent<MarkerManager>();
if (countUp == 0)
{
markM.ClearMarkerList();
}
countUp += Time.deltaTime;
if (countUp >= distanceBetween)
{
GameObject temp = Instantiate(bodyParts[0], markM.markerList[0].position, markM.markerList[0].rotation, transform);
if (!temp.GetComponent<MarkerManager>())
temp.AddComponent<MarkerManager>();
if (!temp.GetComponent<Rigidbody2D>())
{
temp.AddComponent<Rigidbody2D>();
temp.GetComponent<Rigidbody2D>().gravityScale = 0;
}
snakeBody.Add(temp);
bodyParts.RemoveAt(0);
temp.GetComponent<MarkerManager>().ClearMarkerList();
countUp = 0;
}
}
Finally i found the working Solution.
It has nothing to do with which hierarchy order GameObjects spawn in.
Just the Layer and the LayerOrder are responsible for it.
So I give my parent object a specific layer name (manually in the inspector under "Additional Settings" or programmatically)
I chose the programmatic way...
Any newly spawned GameObject that is Child would get a lower number
yourGameObject.GetComponent<Renderer>().sortingLayerID = SortingLayer.NameToID("Player");
yourGameObject.GetComponent<Renderer>().sortingOrder = -snakeBody.Count;
Extremely new to unity and c# after switching across from Python. Once all the balls are scored in my game I want all the 'blockers' (prefabs that I have instantiated) to be removed from the screen and a new set to be spawned randomly on screen in random positions. The blocker prefabs spawn randomly when all balls are scored, however, the old prefabs, besides the one which deletes each time, stay on screen rather than deleting. I have tried looping through the blockers in the code below to delete and I think this is where the issue is as only one game object deletes at this stage:
if (SpawnManager.tempclonecount == 0 )
{
for (int i = 0; i < SpawnManager.blockeramounts; i++)
{
Destroy(gameObject);
}
SpawnManager.tempclonecount = 1;
}
SpawnManager is an empty object which I have used to spawn objects onto the screen, tempclonecount is a variable stating when the old game objects should be removed from the game. This part of the code works well. blockeramounts is the number of prefabs initially on screen and I hoped to loop through the number of prefabs would delete all of the prefabs. It only deletes one. How do I change this?
Here is the code for creating the blockers in spawn manager also, if helpful:
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
}
}
While reading your description of the problem I got a bit confused by some of the terms you used. That's all fine since you are new to Unity and still learning. What i managed to figure out is that you have a SpawnManager script attached to an empty game object which instantiates your blocker prefabs. Then in another script you are getting the SpawnManager refernce and checking if you should destroy the current ones and instantiate a new set.
First of all what i would do is, after instantiating an object, to store it in an array or a list
...
public List<GameObject> blockers = new List<GameObject>();
...
void Start()
{
...
}
void Update()
{
int blockeramount = Random.Range(2, 7);
blockeramounts = blockeramount;
for (int i = 0; i < blockeramount; i++)
{
int blockerindex = Random.Range(0, blockerPrefabs.Length);
var blocker = Instantiate(blockerPrefabs[blockerindex], new Vector3(Random.Range(-30, 30), 0, Random.Range(-30, 30)), blockerPrefabs[blockerindex].transform.rotation);
blockers.Add(blocker);
}
}
After which i would add a new method which does the check to see how many remaining blockers there are. This method should go inside SpawnManager.
public void CheckAndDeleteBlockers()
{
if (tempclonecount == 0 )
{
foreach(var blocker in blockers)
{
blockers.Remove(blocker);
Destroy(blocker);
}
}
}
And you should call it from the other script with:
...
public SpawnManager spawnManager;
...
void Start()
{
spawnManager = FindOjectOfType<SpawnManager>();
}
//for example
void Update()
{
spawnManager.CheckAndDeleteBlockers();
}
I understand the way you are trying to do this, but let's say that this isn't the correct way. I would suggest that you look up what object pooling is.
NOTE: The creator of the pooling tutorial that I mentioned above is a great source for Unity and C# beginners, so I would recommend that you watch some of his other videos. Good luck in learning Unity.
What I believe is causing your issue, is that your are only specifying one gameObject to be destroyed. You need to call Destroy() on each blocker you instantiated.
Hope my advice helps.
So, firstly, my scene is made out of 9 empty objects each one having spikes that have animation to pop out of the floor. I am making a game where you should avoid spikes and other projectiles. I tested this first with sprite renderer and changing colors, it worked perfectly. But when I want to activate animations using trigger (animations start from empty game state and go to their animations, then after it finishes they return to normal). I've looked trough every thing I could think of and I could not solve this. It always starts animations for 4-6 different animations and start them at the same time, but for some reason: AFTER FIRST SHOWING, every single time after first time spikes appear, 2/3 out of 4/6 always show late but start at same time. Weirdly first time it always works greatly. Any thoughts on what may cause this?
void Update()
{
if (spikeTimer > 0)
spikeTimer -= Time.deltaTime;
if (spikeTimer<0)
{
Function();
spikeTimer = spikeCooldown;
}
}
public void Function()
{
int x = Random.Range(4, 6);
List<int> included = new List<int>();
while (included.Count < x)
{
int y = Random.Range(1, 10);
if (!included.Contains(y))
{
included.Add(y);
}
}
foreach (int i in included)
{
print("aktiviran je broj" + i);
Spikes[i - 1].GetComponent<Spike>().ActivateSpike();
}
}
Now this is the ActivateSpike method that all Spikes in the scene contain
public void ActivateSpike()
{
SpriteRenderer sr = gameObject.GetComponent<SpriteRenderer>();
if (sr.color != Color.black)
{
sr.color = Color.black;
}
else
sr.color = Color.red;
/* Animator animator = gameObject.GetComponent<Animator>();
animator.SetTrigger("Activate");*/
}
You can see that I tried changing the sprite to square and changing its color, and it works perfectly...
I have run into a slight issue with the collision resolution in my game engine. If two objects collide and that collision causes the velocity to go to zero, the edges of the objects will overlap each other and they'll be stuck.
Is there a way to implement a catchall for this kind of a situation? i.e. move the objects just enough in the right direction so they are not stuck.
Here is how I am checking collisions and moving objects. When update is called on an entity, it moves the (x,y).
public static void Update()
{
for (var iterator = 0; iterator < PhysicsEntities.Count; iterator++)
{
for (var index = iterator + 1; index < PhysicsEntities.Count; index++)
{
if (!Collision.ResolveCollision(PhysicsEntities[iterator],
PhysicsEntities[index], Detection)) continue;
PhysicsEntities[iterator].Update();
PhysicsEntities[iterator].Collided = true;
PhysicsEntities[index].Update();
PhysicsEntities[index].Collided = true;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}
}
Here is the update function for the entities:
public void Update(bool velocity = false)
{
if(!Movable) return;
if (!Collided)
{
var moveX = Velocity.X / Universe.UpdateInterval;
var moveY = Velocity.Y / Universe.UpdateInterval;
Position.Move(moveX, moveY);
BoundingBox.Move(moveX, moveY);
}
if(velocity) UniversalForces();
}
private void UniversalForces()
{
Velocity.Scale(1 - Universe.Friction);
Velocity.Add(Universe.GravityMag, Universe.GravityDir);
}
Finally, here is a image of one simulation where the objects get stuck. As you can see, it is just the edges that are getting stuck:
The quick solution is to move both objects back to the previous tic's position and any other object that causes a collision with to move back as well. It works, but it looks messy and causes some behavior that looks really bad - things like pushing directly at a wall leaves a gap, but angling towards a wall leaves a smaller gap. Very messy.
The better solution is to move both objects back just far enough along their negative velocity vector so that they are no longer touching. Usually some dot product math can give you what you need for this, though iterating backwards can work (slow).
Long story short, don't ever allow objects to overlap. Take care of it before it happens and you avoid stuck jitters, can't move stuff, etc.
When one object collides with another, get it to backtrack up its own movement vector so that the distance between the centroids of both objects is equal to the two radius. This works if its a circle - if its a complex polygon you really need to do edge collision detection instead of bounding sphere detection. If the bounding spheres collide, then you move to complex edge detection. In the end the trick is the same; check for collision then back up the movement vector until you find the exact (or nearly exact) point of collision.
I was able to figure it out with the suggestions people made. Some of the changes I made include having each object collide with any other object only once per update, and moving the object after the collision until it is no longer colliding. Here is the code that I used to do that, feel free to use this on any project and let me know if you have any questions about it.
public static void Update()
{
foreach (var a in PhysicsEntities)
{
foreach (var b in PhysicsEntities)
{
if (a.Equals(b) ||
!Collision.ResolveCollision(a, b, Detection) || b.Collided) continue;
while (Detection == Detection.BoundingBox ?
Collision.BoundingBox(a, b) :
Collision.PixelPerfect(a, b))
{
const float moveBy = .5F;
var moveX = a.Position.X > b.Position.X ? moveBy : -moveBy;
var moveY = a.Position.Y > b.Position.Y ? moveBy : -moveBy;
if (a.Movable)
{
a.Move(moveX, moveY);
a.Velocity.Scale(-1);
}
else if (b.Movable)
{
b.Move(moveX * -1, moveY * -1);
b.Velocity.Scale(-1);
}
}
a.Update();
b.Update();
a.Collided = a.Movable;
}
}
foreach (var entity in PhysicsEntities)
{
entity.Update(velocity: true);
entity.Collided = false;
}
}
I have 2 images(bar and greenBall1). bar can be move up and down depends on the user response. While, greenBall1 is moving around the screen. I want to do an image collision if both the images touch each other, greenBall1 will change its velocity. The codes that I have for greenBall1 are as below.
private void OnUpdate(object sender, object e)
{
Canvas.SetLeft(this.GreenBall1, this.greenBallVelocityX + Canvas.GetLeft(this.GreenBall1));
Canvas.SetTop(this.GreenBall1, this.greenBallVelocityY + Canvas.GetTop(this.GreenBall1));
var greenBallPositionX1 = Canvas.GetLeft(this.GreenBall1);
var greenBallPositionY1 = Canvas.GetTop(this.GreenBall1);
var maximumGreenBallX = ActualWidth - this.GreenBall1.ActualWidth;
var maximumGreenBallY = 400 - this.GreenBall1.ActualHeight; //Improvise: Instead of 360, get maximum height of canvas
if (greenBallPositionX1 > maximumGreenBallX || greenBallPositionX1 < 0)
{
this.greenBallVelocityX *= -1;
}
if (greenBallPositionY1 > maximumGreenBallY || greenBallPositionY1 < 0)
{
this.greenBallVelocityY *= -1;
}
}
I don't see a reference to the bar object in your code. But the detection of the collision is much easier than the physics of handling the collision. There are several schools of thought to something as simple as Pong collision, and the way you choose to handle it depends on the gameplay you want.
Here is an easy way to detect and handle the collision, simply by negating the X velocity just as you are handling the wall collisions:
if(greenBallPositionX1 < leftBar.X || greenBallPositionX1 > rightBar.X)
{
this.greenBallVelocityX *= -1;
}
Keep in mind you might also have to take into account the width of the bar or the ball depending on where the coordinate is in relation to the image. For example:
if(greenBallPositionX1 < (leftBar.X + leftBar.Width) || greenBallPositionX1 > rightBar.X)
{
this.greenBallVelocityX *= -1;
}
You may also want to at this point move the ball away from the paddle one step to avoid the collision being detected more than once.
Hopefully this answers what you were asking, but if you were looking for a more complex reaction to the collision detection, then you may want to check out the following discussion on Pong type collisions here.
i think this might help you ....
.
Rectangle ballRect = new Rectangle((int)ballposition.X, (int)ballposition.Y, ballsprite.Width, ballsprite.Height);
Rectangle handRect = new Rectangle((int)paddlePosition.X, (int)paddlePosition.Y, paddleSprite.Width, paddleSprite.Height/2);
if (ballRect.Intersects(handRect))
{
// Increase ball speed
ballSpeed.Y += 50;
if (ballSpeed.X < 0)
ballSpeed.X -= 50;
else
ballSpeed.X += 50;
// Send ball back up the screen
ballSpeed.Y *= -1;
}
in this whenever the ball will collide with hand it will increase its speed and change its direction i think that is the think you are looking for as it is also a rectangular collision.
make a class which will give you value that wether two rects will collide or not
public bool Intersects(Rect r1,Rect r2)
{
r1.Intersect(r2);
if(r1.IsEmpty)
{
return false;
}
else
{
return true;
}
}
then you can use
if(Intersects(r1,r2))
{
MessageBox.Show("Collison Detected");
}