How to rotate Unity Quaternion and preserve original axis? - unity3d

I'm working on a simple puzzle-style Unity game for iOS. The game presents the user a "rubiks cube" object (one large cube comprised of smaller cubes).
I want users to be able to swipe left/right/up/down on the cube to rotate as expected.
My code works, but after the user performs a 2nd rotation, the cube does not rotate according to the user's expected direction. After the first rotation, the x/y/z axis have rotated along with the device.
If I only use the left/right swipe code, it works as expected. When I add up/down swiping, it breaks. Which makes sense, since the axis doesn't rotate when I stick to two variables.
Here is my code:
if (Input.GetMouseButtonUp(0))
{
//swipe upwards
if (currentSwipe.y > 0 && currentSwipe.x > -1f && currentSwipe.x < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 0f, 90f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe down
if (currentSwipe.y < 0 && currentSwipe.x > -1f && currentSwipe.x < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 0f, -90f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe left
if (currentSwipe.x < 0 && currentSwipe.y > -1f && currentSwipe.y < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, -90f, 0f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
//swipe right
if (currentSwipe.x > 0 && currentSwipe.y > -1f && currentSwipe.y < 1f)
{
GameObject.FindGameObjectWithTag("Sfx").GetComponent<SoundManager>().PlaySwipe();
gameObject.transform.DORotateQuaternion(Quaternion.Euler(0f, 90f, 0f), 0.5f).SetRelative(true).OnComplete(SetSwiping);
}
}
Any help is appreciated! I'm relatively new to Unity so still learning.

I think it is easier (at least for me) to rethink the setup like this:
private Quaternion _targetRotation; // this will jump to the rotation in discrete steps (i.e. not gradually)
Start(){
_targetRotation = cube.transform.localRotation;
....
}
...
var appliedRotation = Quaterion.identity;
//swipe upwards
if (currentSwipe.y > 0 && currentSwipe.x > -1f && currentSwipe.x < 1f){
appliedRotation = Quaternion.AngleAxis(90f, Vector3.right);
}
... // other swipe cases
_targetRotation = appliedRotation * _targetRotation; // order is important afaik
...
gameObject.transform.DOLocalRotateQuaternion(_targetRotation);
let me know if this works.

Related

I am trying to make a timer in unity for reload using Time.deltaTime. Anyone know why this isn't working?

Here is the code, I'm not sure why this isn't working. I've done things very simmilar before.
float limit = 1f;
float i = 0f;
if(hasResetReload == true && i < limit)
{
i += Time.deltaTime;
}
else if(i == limit)
{
hasResetReload = false;
anim.SetBool("Reload", false);
}
When you write i += Time.deltaTime;, it will assign a 7 digit long float value to i. Now, when you say i == limit, it will have to match all 7 digits on both float which is almost impossible and never become true. You have to use i >= limit. So, it will become true whenever i become greater than or equal to limit.

GetAxis("Mouse X/Y") does not work with virtual mouse

I recently tried writing a bot in python for a unity game to control the character using a virtual mouse like in pyautogui or autohotkey. The game has multiple camera modes. One that uses the cursor location and another that uses GetAxis("Mouse X/Y"). Neither the library I'm using, pyautogui, pydirectinput, or even an autohotkey macro fails to move the mouse when on the option that use GetAxis() although it works on the camera that uses the cursor location. Why is this the case?
Example of a script that fails when using GetAxis():
Yoinked from another stack overflow post since I needed an ahk example as well:
CoordMode, mouse, screen
toggle := 0, fixedY := A_ScreenHeight/2 ; choose the y you like
F1::
MouseGetPos, MouseX, MouseY
if toggle := !toggle
gosub, MoveTheMouse
else
SetTimer, MoveTheMouse, off
return
MoveTheMouse:
Random, x, 1, % A_ScreenWidth
MouseMove, %x%, %fixedY%, 100
Random, Time, 1000*60, 1000*60*5
SetTimer, MoveTheMouse, -%time% ; every 3 seconds
return
The C# code used for movement is:
case CAMERA_TYPE.CAMERA_THAT_WORKS_WITH_BOT:
if (Input.mousePosition.x < (float)Screen.width * 0.4f)
{
mainT.RotateAround(mainT.position, Vector3.up, -(((float)Screen.width * 0.4f - Input.mousePosition.x) / (float)Screen.width * 0.4f) * getSensitivityMultiWithDeltaTime() * 150f);
}
else if (Input.mousePosition.x > (float)Screen.width * 0.6f)
{
mainT.RotateAround(mainT.position, Vector3.up, (Input.mousePosition.x - (float)Screen.width * 0.6f) / (float)Screen.width * 0.4f * getSensitivityMultiWithDeltaTime() * 150f);
}
mainT.rotation = Quaternion.Euler(140f * ((float)Screen.height * 0.6f - Input.mousePosition.y) / (float)Screen.height * 0.5f, mainT.rotation.eulerAngles.y, mainT.rotation.eulerAngles.z);
mainT.position -= mainT.forward * this.distance * this.distanceMulti * this.distanceOffsetMulti;
break;
case CAMERA_TYPE.CAMERA_THAT_FAILS_WITH_BOT:
if (!CustomInputs.Inputs.menuOn)
{
Screen.lockCursor = true;
}
float num5 =0f;
float num6 =0f;
if (((int)GameManager.settings[300]) == 0)
{
num5 = (Input.GetAxis("Mouse X") * 10f) * this.getSensitivityMulti();
num6 = ((-Input.GetAxis("Mouse Y") * 10f) * this.getSensitivityMulti()) * this.getReverse();
}
else if (((int)GameManager.settings[300]) == 1)
{
num5 = (Input.GetAxisRaw("Mouse X") * 10f) * this.getSensitivityMulti();
num6 = ((-Input.GetAxisRaw("Mouse Y") * 10f) * this.getSensitivityMulti()) * this.getReverse();
}
mainT.RotateAround(mainT.position, Vector3.up, num5);
float num7 = mainT.rotation.eulerAngles.x % 360f;
float num8 = num7 + num6;
if (((num6 <= 0f) || (((num7 >= 260f) || (num8 <= 260f)) && ((num7 >= 80f) || (num8 <= 80f)))) && ((num6 >= 0f) || (((num7 <= 280f) || (num8 >= 280f)) && ((num7 <= 100f) || (num8 >= 100f)))))
{
mainT.RotateAround(mainT.position, mainT.right, num6);
}
mainT.position -= (Vector3)(((mainT.forward * this.distance) * this.distanceMulti) * this.distanceOffsetMulti);
break;
Some of this code is incomprehensible due to being decompiled. Also a note, don't worry about modding here as the game has basically been abandoned by the dev for 4 years and is now running on community servers.
After some further searching I found that GetAxis may use the mouse velocity/acceleration and I don't think these mouse move functions affect that in any way. In my use case what I would need for testing is a way to read the mouse acceleration/velocity and a way to move set my mouse's acceleration/velocity. Please let me know if I have the right idea here and any references to material related to this would be greatly appreciated.
The fix I found was to use a windows dll call:
AHK:
DllCall("mouse_event", "UInt", 0x01, "UInt", 100, "UInt", 100)
python:
import ctypes
ctypes.windll.user32.mouse_event(0x01, x, y, 0, 0)

How to find the speed of SKSpriteNode from 5 frames ago in Swift

So I have this Swift code using SpriteKit:
let ballX = ball.position.x
let ballY = ball.position.y
let ballR = ball.size.width / 2
let sceneW = self.size.width
let sceneH = self.size.height
if ballY >= sceneH - ballR - 3 {
framesOnTop += 1
} else {
framesOnTop = 0
}
if framesOnTop > 2 {
// Some code here
}
I have the ball node (SKSpriteNode) that can hit the ceiling and bounce back (scene has no gravity) but sometimes it can hit the ceiling at a very steep angle and get stuck in an infinite loop, left right left right left right. In the if statement that says framesOnTop > 2 I am successfully detecting when it gets stuck, and I want to make it bounce back down at the right angle with physicsBody.applyImpulse. But to do this I need the velocity from before it gets stuck (~5 frames before). Is this possible or is there a better way to fix it?

Square collision detection iPhone

I have this game where i need to know if the ball has hit a wall on the side (to bounce back on the x-axis) or on the top (to bounce back on the y-axis, like a bounce on the ground). They work fine individually, but when I uncomment both of them, it dosen't work. (I think this is because the code is 'overlapping'?). Anyway, here is the code, and any help is fantastic:
if (CGRectIntersectsRect(guy.frame, wall_01.frame)) {
if (guy.frame.origin.y+guy.frame.size.height >= wall_01.frame.origin.y && guy.frame.origin.y <= wall_01.frame.origin.y+wall_01.frame.size.height) {
iJump *= kRestitution;
}
if (guy.frame.origin.x+guy.frame.size.width >= wall_01.frame.origin.x && guy.frame.origin.x <= wall_01.frame.origin.x+wall_01.frame.size.width) {
jJump *= kRestitution;
}
}
assuming wall is on the left side and the y increases from top to bottom
CGFloat leftWall = someXPosition;
CGFloat ground = someYPosition;
CGFloat ballLeft = CGRectGetMinX(guy.frame);
CGFloat ballRight = CGRectGetMaxX(guy.frame);
CGFloat ballBottom = CGRectGetMaxY(guy.frame);
if (ballLeft <= leftwall && ballBot >= ground){
//ball hit corner ?
} else if (ballLeft <= leftWall){
//passed or touched wall
} else if (ballBot >= ground){
//passed or touched ground
}

COCOS2D - Animation Stops During Move

I am trying to run a "Walk" style animation on my main game sprite. The animations work fine, and my sprite is hooked up to my joystick all fine and dandy.
However, I think where I setup the call for my walk animations are wrong. Because everytime the sprite is moving, the animation stops.
I know putting the animation in such a weak if statement is probably bad, but please tell me how I could get my sprite to animate properly while it is being moved by the joystick.
The sprite faces the right direction, so I can tell the action's first frame is being called, however, it doesn't animate until I stop touching my joystick.
Here is How I call the action:
//WALK LEFT
if (joypadCap.position.x <= 69 /*&& joypadCap.position.y < && joypadCap.position.y > >40 */ )
{
[tjSprite runAction:walkLeft];
};
//WALK RIGHT
if ( joypadCap.position.x >= 71 /* && joypadCap.position.y < 100 && >joypadCap.position.y > 40 */)
{
[tjSprite runAction:walkRight];
};
THIS: is the how the joystick controls the character:
CGPoint newLocation = ccp(tjSprite.position.x - distance/8 * cosf(touchAngle),
tjSprite.position.y - distance/8 * sinf(touchAngle));
tjSprite.position = newLocation;
Please help. Any alternative ways to call the characters walk animation would be greatly appreciated!
int current_state;
if (current_state != 1 && joypadCap.position.x <= 69)
{
current_state = 1;
[tjSprite runAction:walkLeft];
}
else if (current_state != 1 && joypadCap.position.x >= 71)
{
current_state = 1;
[tjSprite runAction:walkRight];
}
else
{
current_state = 0;
//[tjSprite stopAllActions];
};
The sprite faces the right direction,
so I can tell the action's first frame
is being called, however, it doesn't
animate until I stop touching my
joystick.
Based on the code you have supplied this actually makes sense. What your if statement is saying is anytime the joypadCap position is greater than 71 or less than 69 play an animation. This means your animations will try to play over and over again from the beginning everytime joypadCap's position falls in these ranges. I assume joypadCap is a measure of how much the joystick is being pressed?
Looks like you could use some additional state logic to determine what your character should be doing. Here is some pseudocode...
state current_state;
if (current_state != walking and joypadCap.position.x <= 69)
{
current_state = walking;
[tjSprite runAction:walkLeft];
}
else if (current_state != walking and joypadCap.position.x >= 71)
{
current_state = walking;
[tjSprite runAction:walkRight];
}
else
{
current_state = idle;
[tjSprite stopAllActions];
}
Keep in mind that is loose pseudocode. Not everything is syntactically correct but logically the idea is that you have a state variable to keep track of the characters current state which allows you so have your animations play one time only. Let me know if this helps and if you have any questions about my answer.