I made a moving platform that goes vertically top to bottom and then bottom to top and so on. Platform moves fine but placing my player on it makes my player unstable.
When platform moves from top to bottom, my player kinda bounces on it. When platform moves from bottom to top, it remain stable on the way but when it reaches top point, my player makes a jump by itself.
Its get even worse when i increase the speed of my platform. I don't know if its due to unity 2d physics effect or what. I have tried to use physics material 2D on my player object and platform by setting bounce to 0 and friction to 50 but nothing seems to work. Any one have idea how to disable physics effect of moving platform? Following is my code for moving platform:
public class BrickMoveVErtical : MonoBehaviour {
public Vector3 positionOne;
public Vector3 positiontwo;
public Vector3 nextposition;
/*Empty object is already made on unity editor and its parent of platform(Plank) and other
empty object "pointB". Point "B" is already mapped on editor and platform is set to go from
its original pos to point B */
public Transform plankTranform;
public Transform positionBTransform;
public float speed;
// Use this for initialization
void Start () {
positionOne = plankTranform.localPosition;
positiontwo = positionBTransform.localPosition;
nextposition = positiontwo;
}
// Update is called once per frame
void Update () {
move();
}
private void move() {
plankTranform.localPosition = Vector3.MoveTowards(plankTranform.localPosition,nextposition,Time.deltaTime*speed);
if(Vector3.Distance(plankTranform.localPosition,nextposition)<0.1)
{ changeMovementPlank(); }
}
void changeMovementPlank() {
nextposition = nextposition != positionOne ? positionOne : positiontwo;
}
}
I had similar problems with fast platforms in my game, the character would be left out in the air or slide off, depending on the direction of the platform.
I found a workaround for the problem by simply changing the character transform.parent to the platform it was jumping on, whenever the player exists the platform collider, simply return the players original transform.parent.
Note that this might cause other bugs or problems that will need to optimize, depending on your game that is.
You can use this code to set/unset the parent of a GameObject:
void OnTriggerEnter2D(Collider2D other)
{
if(other.gameObject.tag == "PlatformGroup")
player.SetParent(other.gameObject.transform)
}
void OnTriggerExit2D(Collider2D other)
{
if(other.gameObject.tag == "PlatformGroup")
player.SetParent(null);
}
What you should do is:
Create an empty GameObject, and you make your platform child of this
empty GameObject. Tag it as PlatformGroup.
You add the script to move the platform to this EmptyGame Object,
instead of using it in the platform itself.
Then add a collider to empty gameobject(PlatformGroup) and another to the Player. Set the one of the player as trigger.
Add the code above to your player controller.
Now, when the Player jumps over the Platform if will become a child of the same GameObject of the platform and they will move together without any deformations in the GameObject. Also when the player leaves the platform, walking or jumping out of it, it will stop being child.
Related
I'm completely new (<1 week) to Unity, C# and game development generally. I'm starting out making a 2D isometric, grid-based CRPG and have hit a major wall with Unity's tilemaps.
I have a component which creates a tilemap tile underneath the parent GameObject using Tilemap.SetTile. As the GameObject moves around the grid, the tile should move with it and delete the previous tile. The tile can then create a moving obstacle node for pathfinding (which is working fine with static obstacles).
The problem is that the script actually does briefly work, just not for very long. If the GameObject moves along a path 'n' cells in a grid, the script will render 'n/2' cells every time the script is run, then stop working altogether. There's no error messages, freezing or anything, the tiles just aren't being drawn, and I know from logging in the console that the script is correctly tracking where the tiles should be drawn.
The obstacle script looks like this:
private void SetObstacleTile()
{
currentCell = obstacleMap.WorldToCell(transform.position);
if (currentCell != previousCell)
{
//set the new tile
obstacleMap.SetTile(currentCell, obstacleTile);
// erase previous
obstacleMap.SetTile(previousCell, null);
// save the new position for next frame
previousCell = currentCell;
Debug.Log(currentCell);
}
}
private void Update()
{
SetObstacleTile();
}
... and the movement currently looks like this:
void Update()
{
if (Vector3.Distance(transform.position, destination) > 0.01f)
transform.position = Vector3.MoveTowards(transform.position, destination, movementSpeed * Time.deltaTime);
}
My gut feeling is that it's something to do with how the Update() functions in the character movement and obstacle tiles component are interacting with each other but I don't have the experience to know how close I am to the problem. Any help with what could be causing this would be massively appreciated.
I could really need some help with my GameObjects.
I am working on a game in which I want a pick-up Item to create a Physics Force explosion to blow away the enemies. I made a simple Bomb-Object to test this idea. I added a straightforward code, using a loop to collect all the colliders within its radius, to then addForce to these colliders. Now the code is working properly, but not all my GameObjects are reacting properly.
I looked into why this is, and I noticed that my Collider keeps falling away from the GameObject as soon as I add a RigidBody component to the object. The RigidBody is needed for the AddForce impact. When I remove the rb, the collider stays in place, but the object does not react to the Physics Force.
I added some images to illustrate what I mean:
Image of the Collider of the leaf sinking away..
Example image of objects which DO react to the AddForce.
I already tried to:
Copy/Paste all component settings from the reacting Cube and stone
Gameobjects to the Leaf Gameobject while disabling all other code
such as the c# scripts. The Collider & RB do not fall through the
floor but when Physics Force hits the Collider is blown away while
the GameObject keeps its position.
Try different GameObjects/Collider types.
Removed the water.
Play with amount of force/radius of the bomb.
Edited 'Tag' and 'Layerstyle' of GameObject.
The 'Explosion Code' is added below, however, I don't think the problem is in the code. I added the code to the Main Camera of my scene, and added a simple sphere as the 'bomb' GameObject.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Explosion : MonoBehaviour
{
public GameObject bomb; //set the position of the explosion
public float power = 10.0f;
public float radius = 10.0f;
public float upForce = 0.0f;
private void FixedUpdate()
{
if (Input.GetKeyDown("space"))
{
print("space key was pressed");
Invoke("Detonate", 1);
}
}
void Detonate()
{
Vector3 explosionPosition = bomb.transform.position; //set the position of our explosion to the position of the bomb.
Collider[] colliders = Physics.OverlapSphere(explosionPosition, radius); //collects all colliders within the radius.
foreach (Collider hit in colliders) { //for each individual collider the following code is ran.
Rigidbody rb = hit.GetComponent<Rigidbody>(); //declare'rb' rigidbody. Get rb component from each collider
if (rb != null)
{
print("BOOM!");
rb.AddExplosionForce(power, explosionPosition, radius, upForce, ForceMode.Impulse); //add force to each collider
}
}
}
}
How do I make the Rigidbody, Collider and GameObject of the leaf hold onto each other like the standard 3D object 'cube', so that I can make these blow away with the Physics Force just like the other models?
Thank you for your time, I have been trying things and looking around on the Internet for hours now but can't seem to find any solution.
What happens if you add the rigidbody in stop mode and press play? Does it move away in a similar manner? This may, and is expected to happen, if your colliders intersect with each other. As soon as you add a rigidbody, they find themselves trapped in a serious collision.
If you don't want to modify the scene, you can fiddle with the collision matrix in project settings / physics
I have an animated sprite which has 8 frames of animation. Each frame has a PolygonCollider2D attached as an element of an array thus providing it with accurate collision detection no matter which frame is playing.
When I had only one collider on the sprite and it passed through the "score" object the player's score increased by 1 point.
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Point")
{
score++;
scoreText.text = score.ToString();
return;
}
}
Polygon Collider 2D array in Unity inspector
Each collider in the array becomes active in turn...
public void SetColliderForSprite(int spriteNum)
{
colliders[batColliderIndex].enabled = false;
batColliderIndex = spriteNum;
colliders[batColliderIndex].enabled = true;
}
Now that each frame of the animation has its own collider I find that the score is increasing at an alarming rate as each frame plays multiple times whilst inside the "score" game object, triggering the point scoring logic multiple times, before the player leaves said object.
I'm wondering what the ideal programmatic solution is in order to ensure that on entering the "point" collider object the player only gets a score increment once before exiting the area?
Thanks in advance for any suggestions (and my apologies if this has been answered elsewhere).
EDIT: I was thinking about my post below and I think there is a simpler solution. You should be able to animate your collider shape rather than having 1 collider per animation frame. For example, see this question on How to update 2d colliders with sprite animation.
(And as a side note, you can also have multiple polygon colliders on each part of the sprite, and animate the colliders to move accordingly. For example, see this post on Animating Polygon Collider 2D. This alternative approach might make the suggestion I make in my post below simpler.)
One suggestion I have is to only increment the score when the first "Point" trigger is detected and then not incrementing the score again until the object has fully left. This can be implemented by keeping track of how many colliders (or which specific colliders) have entered and exited the object. Something like the following:
private const string PointTag = "Point";
private int _triggerCount;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == PointTag)
{
bool firstCollision = (_triggerCount == 0);
if (firstCollision)
{
score++;
scoreText.text = score.ToString();
}
_triggerCount++;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.tag == PointTag)
{
_triggerCount--;
}
}
However, once a collision is in progress, you will likely have to change the behaviour so that all colliders stay active until the object has fully left the collision area. This is because if you disable the collider, the OnTriggerExit2D event will not fire.
This solution may not be acceptable depending on how accurately you need to detect that the VampireBat has left the collision area.
If it doesn't need to be very accurate, you can simplify the solution by adding a 9th collider with a unique tag that covers the entire area of the sprite's animation. By using this 9th collider to determine when the player has left the collision area, you would not need to worry about affecting the code related to showing the appropriate collider per animation frame. (But note that the code example here would have to be modified so that the OnTriggerExit2D uses this 9th collider object.)
If it needs to be more accurate, I think there is likely a solution similar to what I suggested based on which colliders are currently active, the details of your collider shapes & animation frames, and OnTrigerEnter2D, OnTriggerStay, and OnTriggerExit2D. But note that you may also need to be careful that a collision is not re-triggered immediately. (For example, the object may have fully left the collision area, but the next animation frame might trigger a collision again immediately.)
Thanks for the response. Your edit got me to thinking about child objects. I added a child collider (with an index value of 9) then checked for a collision with that.
child object in inspector
Referenced the child collider in the collider array.
Collider array in inspector
After that it was just a matter of referencing it in the player controller script.
// Update is called once per frame
void Update () {
//Player input
if (Input.GetMouseButtonDown(0))
{
bat.velocity = new Vector2(0, 4);
//ADD BAT FLAP SOUND LATER!!!
}
scoreText.text = score.ToString();
Debug.Log("SCORE: " + score);
}
//Point increment function
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Point" && batColliderIndex == 9)
{
score++;
return;
}
}
I now have pixel perfect collision detection against obstacles and a score which increments correctly.
Thanks again.
I want a gameobject spinning around its y-axis. This spinner should have a initial movement direction and when colliding with something, it should change its direction.
I created a little picture to show what I mean, but I want the behaviour for a 3D game.
So I just started with the rotation of the spinner,
public class Spinner : MonoBehaviour
{
[SerializeField]
private float movementSpeed; // speed when moving
[SerializeField]
private float rotationSpeed; // speed when rotating
private Rigidbody rigid;
private Vector3 movementDirection; // the direction the spinner is moving
private void Start()
{
rigid = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
transform.Rotate(0, rotationSpeed, 0); // rotate the spinner around its y-axis
}
private void OnCollisionEnter(Collision col)
{
// set new direction
}
}
How can I move the spinner, that it moves along a direction and whenever it collides with something, it changes its direction. It should never stop moving or rotating.
I would like to point out a few things:
The Unity Physics Engine will make collisions to absorb part of the force which moves your spinner, so unless you keep adding "artificial" forces to the spinner it will eventually stop.
The "air friction" in your scene will also reduce the force of your
spinner, so it will slow it down. You should add a material to the
spinner which has 0 Dynamic Friction
Based on the comment you left in #reymoss' answer, you may consider
to add the bouncy material to the walls, and not to the spinning
GameObject.
To sum up, the issue here is if you want a GameObject to bounce against a wall using the Physics Engine, the object will eventually stop, because some of the forces will be absorbed in each collision. That means you will need to keep adding external forces every time a collision takes place, to keep it moving endlessly.
Something you can try is,
1- Add a bouncy material to the walls and remove the dynamic friction of your spinner. In the following link you can learn about this:
https://docs.unity3d.com/Manual/class-PhysicMaterial.html
2- Add to the Spinner a Collider, so when it detects a collision with a wall (you can tag the walls as so for example) add an additional force to the spinner in the direction it is already moving, so it will compensate the energy lost during the collision.
void OnTriggerExit(Collider other) {
if(other.tag == "wall") {
rigidbody.AddForce(rigidbody.velocity.normalized * Time.deltaTime * forceAmount);
}
}
The idea is to let the Engine decide the direction of the Spinner after the collision with the wall, and as soon as the GameObject leaves the trigger, you add an additional force in the current direction.
Then you can play with the Bounciness of the wall's material and with the forceAmount, until your spinner moves as you have in mind.
Note: Since you will have 2 materials, one in the walls and another in the spinner, maybe playing with the Friction Combine and Bounce Combine you will be able to avoid the force lost during collisions, so you will not need to add the external force I mention in the second step. However I have never tried this.
Let me know if it works. I can't try myself the solution until I arrive home.
If you give the object an initial velocity, attach a collider, create and assign a Physics Material 2D to the collider (to apply bounciness), and attach Colliders to the walls, you can have it bounce around with minimal code.
private void Start()
{
rigid = GetComponent<Rigidbody>();
rigid.velocity = new Vector3(2f, 3f, 1f) // initialize velocity here
}
Ok so I'm completely lost on how to approach this problem. I have a implementation of IPointerClickHandler that goes something like this :
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log(gameObject.name);
//Othe rstuff
}
So at least every object with this attached to it should print out its name to the console. Now I have 2 screenshot to further Explain the Problem.
The two tiles with the lilipop and the briefcase do not fire OnPointerClick when the camera is in this position.
Move the cam a bit to the right and the event is firec correcrtly.
What have I checked/tried:
Nothing is blocking the object
The camera has a 2D Physics Ray caster attached to it. Its layers are correct.
The tiles have BoxCollider2Ds.
Edit: Did a simple raycast test; THis is the code attached to the camera. This hits from any position as long as even part of the collider is vissible on screen. While if even a part of the collider is near the edge of the viewport or outside of it. OnPointerClick does not work. How do I solve this?
public class RayCasterTest : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
Vector3 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(pos, Vector2.up, 0.0f);
if (hit.collider != null)
Debug.Log(hit.collider.gameObject.name);
}
}
Ok so how do I explain this. I have a big 2dBoxcollider behind everything. I use it as camera bounds. Now for some reason from time to time the EventSystem would hit it and not the correct collider that is definitely on top if it. Setting this collider to a layer excluded from the Physics3d raycaster seems to have fixed the problem.