I'm making a turn-based grid-based game with a top-down perspective. Enemies move when the player moves, like a traditional roguelike.
Each character in the game (including the hero and enemies) has a futurePosition attribute that determines where they're supposed to move next. When the player presses a direction key, the hero's futurePosition is set to the direction indicated by the key, then every enemies' futurePosition is updated as well. The code in the Update() function of those characters looks like this :
public virtual void Update() {
if (MustMoveObject()) {
transform.position = Vector3.MoveTowards(transform.position, futurePosition, inverseMoveTime * Time.deltaTime);
} else if (attacking) {
Attack();
futurePosition = Vector3Int.RoundToInt(futurePosition);
}
}
Note: MustMoveObject() returns true if futurePosition and transform.position are not equal
If the game detects there's an enemy in the direction the player is going towards, it will attack it instead. The code looks like this:
MovingObject enemyToAttack = GameManager.instance.CheckForCreatureCurrentPosition(pos);
if (enemyToAttack != null)
{
target = enemyToAttack;
cameraFollow=false;
attacking=true;
animator.SetTrigger("attack");
futurePosition = Vector3.Lerp(transform.position, pos, 0.2f);
} else {
target=null;
cameraFollow=true;
futurePosition = pos;
}
As you can see in the code above, when attacking an enemy, the futurePosition attribute is set at about one fifth (or 20%) of the distance between the hero and the enemy, and "attacking" is set to true. When the player reaches that point in the Update() function, it attacks the enemy and then goes back to its original position. I hope this is clear so far.
Here's my problem : I recently added a test animation for the hero when it attacks, but the animation doesn't start when it should at animator.SetTrigger("attack");. It only activates after the movements of both the hero and the enemies is over. I know that this is the problem because if I replace the line transform.position = Vector3.MoveTowards(transform.position, futurePosition, inverseMoveTime * Time.deltaTime); with transform.position = futurePosition;, it makes the deplacement immediate and the animation triggers instantly. I want the animation to occur while the object is moving towards futurePosition.
Here's what the transition looks like in the inspector (I start from AnyState instead of IdleRight because I just wanted to test things out) :
Screenshot
As you can see, there's no exit time. There's also no delay in the animation itself, I've checked. Everything else is working as expected.
I'm also using Unity version 2019.4.37f1, I'm not sure whether that's relevant.
I hope I made my problem clear and that someone has an idea of what to do. I've searched for a solution, but it didn't seem like anyone had the same issue. If anything, it seemed most people had the opposite problem, where the object wouldn't move as long as the animation was active...
Thanks in advance for your help.
In your animation settings you can toggle loop and the box right under it, also add arguments when you're calling play like that: anim.play("thisAnim", 0, 0.25f) this way it will trigger the animation as soon as you call it, and not after your current anim is finished
Well, I figured it out... It was very dumb.
I needed to check "Has Exit Time" on for the transition between "Any State" and the various Idle States in the inspector. That was literally just it. :/
Related
I want to make sure that various objects moving at high speed cannot pass through walls or other objects. My thought process was to check via Raycast if a collision has occurred between two moments of movement.
So the script should remember the previous position and check via Raycast for collisions between previous and current position.
If a collision has occurred, the object should be positioned at the meeting point and moved slightly in the direction of the previous position.
My problem is that works outside the map not inside. If I go from inside to outside, I can go through the walls. From outside to inside not.
Obviously I have misunderstood something regarding the application with raycasts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObsticalControll : MonoBehaviour
{
private Vector3 positionBefore;
public LayerMask collisionLayer = 9;
private Vector3 lastHit = new Vector3(0, 0, -20);
// Start is called before the first frame update
void Start()
{
positionBefore = transform.position;
}
private void OnDrawGizmos()
{
Gizmos.DrawCube(lastHit, new Vector3(.2f,.2f,.2f));
}
// Update is called once per frame
void Update()
{
Vector3 curruentMovement = transform.position;
Vector2 dVector = (Vector2)transform.position - (Vector2)positionBefore;
float distance = Vector2.Distance((Vector2)positionBefore, (Vector2)curruentMovement);
RaycastHit2D[] hits = Physics2D.RaycastAll((Vector2)positionBefore, dVector, distance, collisionLayer);
if(hits.Length > 0)
{
Debug.Log(hits.Length);
for (int i = hits.Length -1 ; i >= 0 ; i--)
{
RaycastHit2D hit = hits[i];
if (hit.collider != null)
{
Debug.Log("hit");
lastHit.x = hit.point.x;
lastHit.y = hit.point.y;
Vector3 resetPos = new Vector3(hit.point.x, hit.point.y, transform.position.z) + positionBefore.normalized * 0.1f;
transform.position = new Vector3(resetPos.x, resetPos.y, transform.position.z);
}
}
}
positionBefore = transform.position;
}
}
Theres a better way to deal with this that unity has built in.
Assuming the object thats moving at a high speed has a RigidBody(2d in your case) you can set its Collision Detection to Continuous instead of Discrete.
This will help collisions with high speed collision, assuming that its moving at high speed and the wall is not moving.
If for some reason you cannot apply this to your scenario, Ill try to help with the raycast solution.
However, I am still wondering about the collision behavior of my raycast script. That would be surely interesting, if you want to calculate shots or similar via raycast
Alright, so your initial idea was to check if a collision had occurred, By checking its current position and its previous position. And checking if anything is between them, that means a collision has occurred. And you would teleport it back to where it was suppose to have hit.
A better way todo this would be to check where the GameObject would be in the next frame, by raycasting ahead of it, by the distance that it will travel. If it does hit something that means that within the next frame, it would have collided with it. So you could stop it at the collision hit point, and get what it has hit. (This means you wouldn't have to teleport it back, So there wouldn't be a frame where it goes through then goes back)
Almost the same idea but slightly less complicated.
Problem would be that if another object were to appear between them within the next frame aswell, it could not account for that. Which is where the rigidbody.movePosition shines, And with OnCollisionEnter you can detect when and what it collided with correctly. Aswell as without the need to teleport it back
I'm following this video https://www.youtube.com/watch?v=THnivyG0Mvo and This is my shoot function
void Shoot()
{
muzzleFlash.Play();
shootingSound.Play();
RaycastHit hit;
if( Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
{
//Debug.Log(hit.transform.name);
Enemy1 target = hit.transform.GetComponent<Enemy1>();
if (target != null)
{
target.TakeDamage(damage);
}
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce( - hit.normal * impactForce);
}
GameObject impactGo = Instantiate(impactEffect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(impactGo, 0.3f);
}
}
Rigidbody added to target:
Barrel component:
enter image description here
This function is in my Rifle.cs script which is being added to a rifle object. Everything works fine. But, when I hit an object which has a Rigidbody, it doesn't move but I can see in the scene that the Rigidbody is moving when I hit it many times. The Rigidbody of the target is set to 'Use Gravity' and 'Is Kinematic' is not checked. What am I doing wrong ?
Probably the force you are adding is too small, so it needs a lot of shots to make some effect, as #Horothenic says, try to increase the value of the impactForce Variable. Look if the rigidbody, the mesh renderer, and the colliders are attached to the same object in the scene. The title of your question suggests your rigidbody is moving but your render doesn't change.
Give the force a ForceMode. Since you want the object to look shot, I would recommend using impulse and make sure the force is bigger than the objects mass by a big factor.
if(hit.rigidbody != null)
{
Debug.Log("******************");
hit.rigidbody.AddForce((-hit.normal * impactForce), ForceMode.Impulse);
}
See this page for more info. Force Modes in Unity
Try removing the Animator completely, the animations have an option to write defaults, if the movement was edited on animation it misbehaves because it wants to set the default.
After I have done a lot of google search. This answered my question https://forum.unity.com/threads/mesh-renderer-does-not-move-with-parent-rigid-body.204700/. So What I needed to do it to turn off (unchecked) static in the top right of the inspector for the object.
enter image description here
I am currently building a little 2D platformer with a character that can jump. This is how the jumping looks (FixedUpdate):
if (jump)
{
if (isGrounded)
{
isGrounded = false;
rb.AddForce(Vector2.up * (jumpHeight * counterForJumpHeight) * Time.deltaTime, ForceMode2D.Impulse);
jump = false;
anim.SetBool("bool_anim_isJumping", true);
}
if (timer != null)
timer.Stop();
counterForJumpHeight = jumpMulitMin;
jumpAlreadCharging = false;
}
Looks perfect for every jump up and then falling back down.
HOWEVER: when the player JUST falls (like off a cliff oder something) without a jump it looks like he has the mass of a leaf. Sailing to the ground extremley slowly. Not accelerating at all. Just falling as in slow motion. Of course I can up the gravity, but that also affects the falling AFTER my jump and makes him look like a stone. As if the falling is sped up or something. But that doesnt make sense. Him falling AFTER a jump and him just falling off of something SHOULD look the same, right? But it doesnt.
These are my values for the RB:
YOu can either up your player's rigidbody Mass, or increase gravity in edit/projectsettings/ physics
I'm not 100% sure but this may have to do with the collision detection. Try changing the setting to continuous dynamic instead of discrete.
It can be a problem with the animator, if the animation contains changes to Rigidbody, it does weird stuff if you have set the Make Default in animation.
it was all my fault, there was no way of guessing it from your point of view.
Everything the player could jump from was tagged with "can_jump".
void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.tag == "can_jump") // detect collision with ground game object
{
isGrounded = true;
deacceleratePerFrame = 1.5f;
anim.SetBool("bool_anim_isJumping", false);
}
}
This includes all edges. Removing this means I cannot jump atm anymore, however this caused the issue. I sure find another way :-) thank you all
I've created an arm with a custom pivot in Unity which is essentially supposed to point wherever the mouse is pointing, regardless of the orientation of the player. Now, this arm looks weird when pointed to the side opposite the one it was drawn at, so I use SpriteRenderer.flipY = true to flip the sprite and make it look normal. I also have a weapon at the end of the arm, which is mostly fine as well. Now the problem is that I have a "FirePoint" at the end of the barrel of the weapon, and when the sprite gets flipped the position of it doesn't change, which affects particles and shooting position. Essentially, all that has to happen is that the Y position of the FirePoint needs to become negative, but Unity seems to think that I want the position change to be global, whereas I just want it to be local so that it can work with whatever rotation the arm is at. I've attempted this:
if (rotZ > 40 || rotZ < -40) {
rend.flipY = true;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
} else {
rend.flipY = false;
firePoint.position = new Vector3(firePoint.position.x, firePoint.position.y * -1, firePoint.position.z);
}
But this works on a global basis rather than the local one that I need. Any help would be much appreciated, and I hope that I've provided enough information for this to reach a conclusive result. Please notify me should you need anything more. Thank you in advance, and have a nice day!
You can use RotateAround() to get desired behaviour instead of flipping stuff around. Here is sample code:
public class ExampleClass : MonoBehaviour
{
public Transform pivotTransform; // need to assign in inspector
void Update()
{
transform.RotateAround(pivotTransform.position, Vector3.up, 20 * Time.deltaTime);
}
}
I have set up Unity navigation meshes (four planes), navigation agent (sphere) and set up automatic and manual off mesh links. It should now jump between meshes. It does jump between meshes, but it does that in straight lines.
In other words, when agent comes to an edge, instead of actually jumping up (like off mesh link is drawn) it just moves straight in line but a bit faster. I tried moving one plane higher than others, but sphere still was jumping in straight line.
Is it supposed to be like this? Is it possible to set up navigation to jump by some curve? Or should I try to implement that myself?
I came by this question, and had to dig through the Unity sample. I just hope to make it easier for people by extracting the important bits.
To apply your own animation/transition across a navmesh link, you need to tell Unity that you will handle all offmesh link traversal, then add code that regularly checks to see if the agent is on an offmesh link. Finally, when the transition is complete, you need to tell Unity you've moved the agent, and resume normal navmesh behaviour.
The way you handle link logic is up to you. You can just go in a straight line, have a spinning wormhole, whatever. For jump, unity traverses the link using animation progress as the lerp argument, this works pretty nicely. (if you're doing looping or more complex animations, this doesn't work so well)
The important unity bits are:
_navAgent.autoTraverseOffMeshLink = false; //in Start()
_navAgent.currentOffMeshLinkData; //the link data - this contains start and end points, etc
_navAgent.CompleteOffMeshLink(); //Tell unity we have traversed the link (do this when you've moved the transform to the end point)
_navAgent.Resume(); //Resume normal navmesh behaviour
Now a simple jump sample...
using UnityEngine;
[RequireComponent(typeof(NavMeshAgent))]
public class NavMeshAnimator : MonoBehaviour
{
private NavMeshAgent _navAgent;
private bool _traversingLink;
private OffMeshLinkData _currLink;
void Start()
{
// Cache the nav agent and tell unity we will handle link traversal
_navAgent = GetComponent<NavMeshAgent>();
_navAgent.autoTraverseOffMeshLink = false;
}
void Update()
{
//don't do anything if the navagent is disabled
if (!_navAgent.enabled) return;
if (_navAgent.isOnOffMeshLink)
{
if (!_traversingLink)
{
//This is done only once. The animation's progress will determine link traversal.
animation.CrossFade("Jump", 0.1f, PlayMode.StopAll);
//cache current link
_currLink = _navAgent.currentOffMeshLinkData;
//start traversing
_traversingLink = true;
}
//lerp from link start to link end in time to animation
var tlerp = animation["Jump"].normalizedTime;
//straight line from startlink to endlink
var newPos = Vector3.Lerp(_currLink.startPos, _currLink.endPos, tlerp);
//add the 'hop'
newPos.y += 2f * Mathf.Sin(Mathf.PI * tlerp);
//Update transform position
transform.position = newPos;
// when the animation is stopped, we've reached the other side. Don't use looping animations with this control setup
if (!animation.isPlaying)
{
//make sure the player is right on the end link
transform.position = _currLink.endPos;
//internal logic reset
_traversingLink = false;
//Tell unity we have traversed the link
_navAgent.CompleteOffMeshLink();
//Resume normal navmesh behaviour
_navAgent.Resume();
}
}
else
{
//...update walk/idle animations appropriately ...etc
Its recommended to solve your problems via animation. Just create a Jump animation for your object, and play it at the correct time.
The position is relative, so if you increase the Y-position in your animation it will look like the object is jumping.
This is also how the Unity sample is working, with the soldiers running around.
Not sure what version of unity you are using but you could also try this, I know it works just fine in 4:
string linkType = GetComponent<NavMeshAgent>().currentOffMeshLinkData.linkType.ToString();
if(linkType == "LinkTypeJumpAcross"){
Debug.Log ("Yeah im in the jump already ;)");
}
also just some extra bumf for you, its best to use a proxy and follow the a navAgent game object:
Something like:
AIMan = this.transform.position;
AI_Proxy.transform.position = AIMan;
And also be sure to use:
AI_Proxy.animation["ProxyJump"].blendMode = AnimationBlendMode.Additive;
If you are using the in built unity animation!
K, that's my good deed for this week.
Fix position in update()
if (agent.isOnOffMeshLink)
{
transform.position = new Vector3(transform.position.x, 0f, transform.position.z);
}