im having trouble with the hand IK of a -recoil shoot animation- that is on an override layer in the animator. I have it set up so that when I aim(left trigger), it allows me to shoot with another button(right trigger). I have it so the weight (hand IK) increases to 1 (in the rig layer) when I aim and decreases to 0 when I let go of the aim button.
The problem is that if I shoot the gun (uses recoil gun animation) and let go of the aim button immediately afterwards, the animation will continue to play but the IK will go to 0 and the hands will look all messed up.
I've tried setting the IK hand weight to 1 when he fires the gun, so he'll still hold onto it, but it doesn't work since shoot is only true if aim is being held down.
So overall I fire the gun, he does a recoil animation, and when you let go of aim too early, the animation looks all messed up like he's waving to you from behind his back. Like it aborts the animation or something.
private void HandleMovementInput()
{
horizontalMovementInput = movementInput.x;
verticalMovementInput = movementInput.y;
animatorManager.HandleAnimatorValues(horizontalMovementInput, verticalMovementInput, runInput);
if (verticalMovementInput != 0 || horizontalMovementInput !=0) //running
{
if (aimingInput)
{
if (animatorManager.isAimingGunn == false)
{
shootInput = false;
}
}
}
if (animatorManager.isAimingGunn)
{
animatorManager.leftHandIK.weight = 1; // IK weight
animatorManager.rightHandIK.weight = 1;
horizontalMovementInput = 0f; // joystick
verticalMovementInput = 0f;
}
else if(animatorManager.isAimingGunn == false)
{
if(verticalMovementInput != 0 || horizontalMovementInput !=0) //running
{
animatorManager.leftHandIK.weight = 0;
}
animatorManager.leftHandIK.weight = 0; // IK weight
animatorManager.rightHandIK.weight = 0;
}
}
private void HandleShoot()
{
if (aimingInput) // must aim otherwise shootInput(right trigger on controller) wont work
{
if (shootInput)
{
shootInput = false;
playerManager.UseCurrentWeapon();
}
}
}
I was able to fix it. I used this code to tell the character to keep aiming if still in the current animation.This allows for the animation to finish before you set the Hand IK to 0.
if(animator.GetCurrentAnimatorStateInfo(1).IsName("Pistol_Shoot"))
{
aimingInput = true;
}
Also Due to it getting overly complicated I changed the aim cancel button to the top left trigger on the controller ( while aim is on the bottom left trigger).
Related
I am trying to create a procedural gun controller, but I can't find why my gun behaves so weird when I change the fire mod to automatic. To understand this better, I will give an example: when I try to shoot by pressing the "Mouse0" key, on semi auto mod it works fine, it behaves like I want it to (the bolt kicks back when I shoot, the front barrel kicks back - I animate them with code instead of frame animations from Unity or a 3rd party soft like Blender), but..., when I change to automatic, the problem is not that I get errors, The animations of the moving pieces don't work as they should and are not triggered correctly.
I tried to use different methods for shooting(using the WaitForSeconds() and WaitForSecondsRealtime() while using coroutines). It didn't work. I tried using the time function with scaled and unscaled time in the update function. I still got the same results.
What should I do to?
This is the shoot function untill now:
void GunFireManagement()
{
if (fireType == fireMethod.single)
{
foreach (BlowBack anim in animations)
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
gunSoundClone = Instantiate(gunShootSound, this.transform.position, Quaternion.identity) as GameObject;
anim.piece.transform.position = anim.kickbackState.transform.position;
}
if (anim.piece.transform.position != anim.initialPosition.transform.position)
{
anim.piece.transform.position = Vector3.Lerp(anim.piece.transform.position, anim.initialPosition.transform.position, anim.speed);
}
Destroy(gunSoundClone, 0.5f);
}
}
if (fireType == fireMethod.auto)
{
foreach (BlowBack anim in animations)
{
if (Input.GetKey(KeyCode.Mouse0) && Time.time - lastFired > 1f/autoFireRate)
{
lastFired = Time.time;
gunSoundClone = Instantiate(gunShootSound, this.transform.position, Quaternion.identity) as GameObject;
anim.piece.transform.position = anim.kickbackState.transform.position;
}
if (anim.piece.transform.position != anim.initialPosition.transform.position)
{
anim.piece.transform.position = Vector3.Lerp(anim.piece.transform.position, anim.initialPosition.transform.position, anim.speed);
}
Destroy(gunSoundClone, 0.5f);
}
}
}
The issue is how you are using Vector3.Lerp. The first two arguments you pass to the method are supposed to be the start and end positions of the animation, and the third one, t, is supposed to be the progress of the animation from the start towards the end, as a value between 0 and 1.
You can calculate the value of t by dividing the time since the shot started with the duration of the animation. For example if the length of the animation is 2 seconds, and the short started 1 second ago, then t should be 0.5.
if(isFiring)
{
float timeSinceShotStart = Time.deltatime - lastFired;
// t = s / v
float animationDuration = 1f / anim.speed;
UpdateBlockBackAnimationState(timeSinceShotStart / animationDuration);
}
}
private void SetBlowBackAnimationState(float progress01)
{
foreach(BlowBack anim in animations)
{
Vector3 initialPosition = anim.initialPosition.transform.position;
Vector3 finalPosition = anim.finalPosition.transform.position;
anim.piece.transform.position = Vector3.Lerp(initialPosition, finalPosition, progress01);
}
}
I recommend you try to split up your code into smaller methods that are easier to understand. You are trying to do so many things in one generic "GunFireManagement" method that it becomes pretty difficult to keep track of all the different pieces :)
I also recommend considering using a tweening framework such as DOTween to make it easier to animate things over time.
I've implemented opening drawers/doors features on my game but the issue I'm facing is that when The player opens a drawer, it gets pushed back so the drawer has some room to be opened. sometimes it jitters when pushed back.
Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, 3f);
if (hit.transform)
{
interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
}
else
{
lookObject = null;
interactiveObjects = null;
}
if (Open)
{
if (interactiveObjects)
{
interactiveObjects.OnOpen();
}
}
I'm using raycast to open the drawer. Is there a way to only allow the raycasting, when the player is not too close to the drawer? so it doesn't get pushed back by the drawer.
You can check the distance after doing the raycast. If the distance is within the tolerable range, execute the rest of your code.
if (Physics.Raycast(mainCamera.transform.position, mainCamera.transform.forward, out hit, 3f))
{
if (hit.distance >= minDistance)
{
// Code to execute when range is acceptable
}
else
{
Debug.Log("Player is too close to object!");
}
}
The distance used above does not take into account the height difference between the hit point and the camera. You can get a much more consistent distance by setting the y component of both vectors equal before getting the distance.
var cameraPos = mainCamera.transform.position;
cameraPos.y = hit.point.y;
var distance = Vector3.Distance(hit.point, cameraPos);
So i'm currently making a game, and i've recently added a level editor, but the placing tool does not work how i wanted it to.
https://youtu.be/MuUvnVTL6eg
If you've watched this video, you've probably realized that the block placing works pretty much how placing rectangles in ms pain with alt does, and i want it to work like placing rectangles in ms pain without alt xd.
I'm using this code to place the block:
if (Input.GetKeyDown(KeyCode.Mouse0)){
startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
tmpObj = spawnObject(blocks[selected].gameObject, startDrawPos);
drawing = true;
}
if (Input.GetKey(KeyCode.Mouse0)){
Vector2 mPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector2 tmpScale = new Vector2(startDrawPos.x - mPos.x, startDrawPos.y - mPos.y);
tmpObj.transform.localScale = tmpScale;
}
if (Input.GetKeyUp(KeyCode.Mouse0))
{
drawing = false;
var scale = tmpObj.transform.localScale;
//Code below destroys the object if it's too small to avoid accidental placements
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1)
{
Destroy(tmpObj);
}
}
(All of this code is in the Update() function)
(spawnObject function just instantiates the object prefab)
There is a bit more code but it has nothing to do with the position of the block, it just detect which block is selected and decides if it can be resized or not.
I solved this problem. But because your complete script is not in question, I rebuilt the code with IEnumerator, Here, by pressing the left mouse button, IEnumerator is activated and all commands are grouped in one method to make the code more efficient.
private void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0)) StartCoroutine(DrawRect());
}
How does the Desktop Rect formula work?
By running IEnumerator, the code first records the starting point of the mouse. It also makes a simple cube because I do not have access to your objects. Now until the mouse is pressed. Resize Rect to the difference between current and recorded points. The only thing is that to avoid ALT control, you have to place it between the current and initial points. The reason for adding the camera forward is to be seen in the camera.
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2;
The final structure of the DrawRect is as follows:
public IEnumerator DrawRect()
{
drawing = true;
var scale = Vector2.zero;
var startDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
var cubeObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
while (Input.GetKey(KeyCode.Mouse0))
{
var currentDrawPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
cubeObject.transform.position = (startDrawPos + currentDrawPos) / 2 + Camera.main.transform.forward * 10;
scale = new Vector2(startDrawPos.x - currentDrawPos.x, startDrawPos.y - currentDrawPos.y);
cubeObject.transform.localScale = scale;
yield return new WaitForEndOfFrame();
}
if (scale.x <= 0.1 && scale.x > -0.1 || scale.y <= 0.1 && scale.y > -0.1) Destroy(cubeObject);
drawing = false;
}
I'm making a simple 2d pong game. I finished everything now the only problem I have is that the GUI exit or restart button clicks are being recorded when it should not be recorded. Lets say both players fingers are on the screen and they move them so one players finger is 25 units from the button on the x axis and the others -25. These 2 finger touches somehow record as one finger touch in the middle of the screen where my exit button is and the app closes. I have 2 scripts for player controls one for Player 1 and other for player 2
var speed : float = 10;
function Update () {
if (Input.touchCount > 0)
{
var touchDeltaPos:Vector2 = Input.GetTouch(0).position;
if(Input.touchCount>1)
var touchDeltaPos2:Vector2 = Input.GetTouch(1).position;
if(touchDeltaPos.x<Screen.width/2)
{
if(touchDeltaPos.y > Screen.height/2)
{
rigidbody2D.velocity.y = 1*speed;
}
else rigidbody2D.velocity.y = -1*speed;
}
else if(touchDeltaPos2.x<Screen.width/2&&Input.touchCount>1)
{
if(touchDeltaPos2.y > Screen.height/2)
{
rigidbody2D.velocity.y = 1*speed;
}
else rigidbody2D.velocity.y = -1*speed;
}
}
if (Input.touchCount == 0)
rigidbody2D.velocity.y = 0;
rigidbody2D.velocity.x=0;
}
I don't know where your buttons are, but in any case how about making a buffer so it won't be touched by accident like
width = Screen.width/2 - 100
or minus 100 whichever works, the buffer is supposed to take in account the width and height of the finger as well
EDIT
When I say buffer, it means a temporary storage like a variable. So for the code you could do:
var buffer:int = 100;
(touchDeltaPos.x < Screen.width/2 - buffer)
Now why do this? Well, the buffer gives more room for the player's playing area from the exit button.
I am developing my first game with the Unity3D engine and I have run into my first problem! Not as exciting as I thought. If I spam the jump button w my ball jumps the first time then when it lands it does not jump right away but it jumps randomly after a number of button presses. The code I am using is below.
#pragma strict
var rotationSpeed = 100;
var jumpHeight = 8;
private var isFalling = false;
function Update ()
{
//Handle ball rotation.
var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque (Vector3.back * rotation);
if (Input.GetKeyDown(KeyCode.W) && isFalling == false)
{
rigidbody.velocity.y = jumpHeight;
}
isFalling = true;
}
function OnCollisionStay ()
{
isFalling = false;
}
I heard this was a arithmetic behavior problem in UnityScript. I am a very beginner at programming and do not really understand the code in UnityScript and this is the first game/project I am making in Unity3D. Help would be greatly appreciated. Thanks!
You would think that something as simple as jumping should be a no-brainer, but there are a couple of gotchas here:
It can happen that Update() is called twice without any physics updates in between. This means you don't receive OnColliderEnter nor OnColliderStay in between but you still reset isFalling, blocking the jump key.
The moment the ball hits you will receive OnColliderEnter, but not OnColliderStay. This won't happen until the next physics update. If you only listen to -stay you will be one update late.
If you press jump right at the time the ball is supposed to hit the ground then your key is already down when the first collider hit registers. You will be another update late.
Objects sink a little bit into the collider. When you jump, it might take a couple of updates before you clear the collider and you will receive OnColliderStay after you jumped.
I think that last point is the biggest problem here. To solve this you need to do a couple of things:
Use both OnColliderEnter and OnColliderStay. This will imrpove your timing with one update.
Reset isFalling in FixedUpdate instead of in Update. FixedUpdate is part of the physics loop, so it is called right before OnCollisionEnter and OnCollisionStay who will set it again immediately if needed.
Remember that you are trying to jump until you are actually in the air. This allows you to clear the collider with one button press.
The code below implements these points. If you want the timing to be even tighter you must queue the next jump while you are in the air.
#pragma strict
var rotationSpeed = 100;
var jumpHeight = 8;
private var isFalling = false;
private var tryingToJump = false;
function Update ()
{
//Handle ball rotation.
var rotation : float = Input.GetAxis ("Horizontal") * rotationSpeed;
rotation *= Time.deltaTime;
rigidbody.AddRelativeTorque (Vector3.back * rotation);
if (Input.GetKeyDown(KeyCode.W) && !isFalling) tryingToJump = true;
if (tryingToJump)
{
if (isFalling) tryingToJump = false;
else rigidbody.velocity.y = jumpHeight;
}
}
function FixedUpdate()
{
isFalling = true;
}
function OnCollisionStay()
{
isFalling = false;
}
function OnCollisionEnter()
{
isFalling = false;
}