(For anyone that understands, this is a simulator for an Omnitrix from Ben10 lol)
Explanation: I am trying to create a dial-like program. When an arrow key is pressed, an animation for a dial rotating plays, but it resets to the default position right after. The animation rotates the dial 20 degrees to the right or left, depending on the key pressed. How do I make the object stay at the 20-degree angle after the animation has played, and have the next animation start at the 20-degree angle and rotate to 40 degrees?
What I tried was to have the animation play and then change the rotation angle of the object to 20 degrees directly after so that it could increase to 40 from there, but that did not work (for reasons I have not figured out).
Code:
Ignore the inner if and print statements; those work as intended for debugging other areas of the project. The transform.Rotate statement at the end is the way that I tried to have my idea work, but it was unfunctional.
void Update()
{
if (Input.GetKeyDown("d") || Input.GetKeyDown("right"))
{
anim.Play("twistRight");
if (numAliens >= 17)
{
numAliens = 0;
}
else
{
numAliens++;
}
print("right" + numAliens);
twistObject.transform.Rotate(0, 20, 0);
}
if (Input.GetKeyDown("a") || Input.GetKeyDown("left"))
{
anim.Play("twistLeft");
if (numAliens <= 0)
{
numAliens = 17;
}
else
{
numAliens--;
}
print("left" + numAliens);
twistObject.transform.Rotate(0, -20, 0);
}
}
Animator: I think that the fact that the animations transition into the default state is what is causing this, but I do not know how else to have this program work under my current (unfunctional) structure of code. Without transitioning into the default state, it does not let me play the same animation multiple times consecutively, so I felt that this was the only option.
Any and all help is greatly appreciated. Thank you in advance!
Link to demonstration: https://drive.google.com/file/d/1I5Z2lQ-R9nWzC2rQ7Z389YGldmQcaeU1/view?usp=sharing
Related
Still pretty new to Unity and C#. I can usually find my way around when it comes to setting/changing the position of an object, but when it comes to rotation it's still mind boggling to me, with quaternion, eulerangles etc..
I'm trying to check if an object (a head) is facing a certain direction, if it's not facing that direction it needs to set it to that direction depending which way it goes (up, down, left or right). This is for a snake game (like the old top down nokia game) I'm currently developing.
I tried to look it up, but none of the answers are specific to what I'm trying to do.
As of right now this is what I have for player input (a snippet of code):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left)
{
snakeHeadRotation.transform.Rotate(0, 180, 0); // Keeps rotating when left is pressed
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As you can see the snakeHeadRotation.transform.Rotate(0, 180, 0); is getting called each time the player presses the left key for instance, thus turning the head of the snake even when the snake is still traveling to the left.
My logic would be to store the rotation value somewhere (type Quaternion/bool maybe) and check to see if that rotation value matches up with the current rotation value set to left (which is snakeHeadRotation.transform.Rotate(0, 180, 0);) If that's the case, don't do anything. If it isn't, set it to snakeHeadRotation.transform.Rotate(0, 180, 0); for instance.
the code would look something like (writing it out):
private int GetAxisRaw(Axis axis)
{
if (axis == Axis.Horizontal)
{
bool left = Input.GetKeyDown(KeyCode.LeftArrow) || Input.GetKeyDown(KeyCode.A);
bool right = Input.GetKeyDown(KeyCode.RightArrow) || Input.GetKeyDown(KeyCode.D);
if (left && rotation is not equal to snakeHeadRotation.transform.Rotate(0, 180, 0))
{
snakeHeadRotation.transform.Rotate(0, 180, 0);
return -1;
}
if (right)
{
snakeHeadRotation.transform.Rotate(0, -180, 0);
return 1;
}
return 0;
}
As said, I'm very unfamiliar with setting or storing rotation values. How can I achieve this? Is this even the right approach or is there a more logical one? I can't figure this one out myself..
I hope I explained it right, I'm usually terrible at explaining things.
Using Transform.Rotate(x) will rotate your head by x degrees relatively to your current head direction.
What you want I suppose is to set the absolute rotation of your head depending on the arrow key you pressed. Thus, even if you press multiple times the same arrow key, the head will stay in the correct direction.
To do that you can create 4 rotations for each head direction and so use them as needed.
For example, if your head is facing forward by default:
// create your directions
private static Quaternion forwardDirection = Quaternion.Identity;
private static Quaternion rightDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.right);
private static Quaternion leftDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.left);
private static Quaternion backDirection = Quaternion.FromToRotation(Vector3.forward, Vector3.back);
...
private int GetAxisRaw(Axis axis)
{
...
if (left)
{
// set the absolute direction of your head to the left
snakeHeadRotation.transform.rotation = leftDirection;
return -1;
}
if (right)
{
// set the absolute direction of your head to the right
snakeHeadRotation.transform.rotation = rightDirection;
return 1;
}
...
}
I would just create 4 quaternions for each direction and set the rotation according to the key pressed (so like hear.rotation = leftQ). This is the simplest way.
I am away from my PC currently but I will try to find another solution when I can
You could compare the current rotation to the known rotations for the 4 directions.
But don't go that way, it's bad practice for several reasons:
what if later you want to add animations, e.g. the head turns in .5 seconds, not instantly?
comparing 4 floats only to check one of 4 possible directions is inefficient.
Create a enum instead:
public enum Direction
{
Up,
Down,
Left,
Right
}
Add a member of this type to your behavior, to keep track of where your snake head is going.
Something like:
Direction direction;
...
private int GetAxisRaw(Axis axis)
{
switch (direction)
{
case Direction.Up:
if (left && !right)
direction = Direction.Left;
... turn snake left ...
else if (right && !left)
direction = Direction.Right;
... turn snake right ...
// ignore inputs for up or down if going up
break;
... repeat for the other directions and possible inputs ...
}
...
}
I have a condition that makes a check. Like this:
if (moveDown)
{
Debug.Log("hit1");
if (RightTowers[rightIndex].transform.GetChild(2).transform.eulerAngles.z <= -40f)
{
moveDown = false;
moveUp = true;
}
else
{
RightTowers[rightIndex].transform.GetChild(0).gameObject.GetComponent<SpriteRenderer>().sprite = Mirrors[mirrorIndex++];
rotateAngle = Quaternion.Euler(0f, 0f, RightTowers[rightIndex].transform.GetChild(2).eulerAngles.z - angle);
RightTowers[rightIndex].transform.GetChild(2).transform.rotation = rotateAngle;
}
}
if (moveUp)
{
Debug.Log("hit2");
if (RightTowers[rightIndex].transform.GetChild(2).transform.eulerAngles.z >= 40f)
{
moveDown = true;
moveUp = false;
}
else
{
RightTowers[rightIndex].transform.GetChild(0).gameObject.GetComponent<SpriteRenderer>().sprite = Mirrors[mirrorIndex--];
rotateAngle = Quaternion.Euler(0f, 0f, RightTowers[rightIndex].transform.GetChild(2).eulerAngles.z + angle);
RightTowers[rightIndex].transform.GetChild(2).transform.rotation = rotateAngle;
}
}
The problem is that when the object is rotated to -40 degrees, and the condition is checked it does not deactivate moveDown and activate moveUp. He further rotates the object with angle index.
I do this rotation when I click on a button. He must, when he reaches -40 degrees, deactivate me moveDown. Why not disable it?
First things first, create some intermediate fields, your code is really heavy, and you're actually accessing the data each time because of all the nested calls you are making, and it's really making it unclear, especially for anyone outside of your project.
Secondly, I don't see the point of having 2 booleans which are the exact opposite value of each other.. you'd get the same result having just one since they are inversed.
That being said, I believe (from what I got from your snippet) that you're probably not in the degree range you might think ? I don't know if you have verified that but, your character could look totally normal, exactly like you have place him originally, but he might have a 360, 720 etc... degree rotation applied(can be negative too), so if you are not looking at the rotation between initial position and desired, you might not get the right values, maybe do a RotationDifference() func, or if (rotation == 360 or -360) rotation = 0 I'm just throwing out ideas, you really have many ways of doing it.
Lastly, your code seems quite heavy for a seemingly mundane task, you should look into that. Try to abstract your tasks, you are trying to do everything at once, instead of having sturdy systems taking care of it.
(new answer after comments)
The logic you want is not appearing "clearly" while reading the code, this makes difficult to debug and understand what is wrong !
As per your comment, I understand that you need move back and forth from -40 to 40.
First, we need to clearly separate the tasks
The two separate tasks are :
Decide in which direction tower is moving next step.
Move the tower up or down. This is basically the two 3-lines blocks of code. Let's call these blocks "MoveUp()" and "MoveDown()"
Now, write some pseudo code for it
// pseudo code
// First, decide in which direction to go
if current angle is 40° (or more),
set direction to down for next move.
else if current angle is -40 (or less),
set direction to up for next move.
else
just continue in the current direction
// Second, move the tower
if direction is down, move down
else move up.
Let's translate this to real C# code
// suppose you have a private bool variable in your class
// called "isMovingDown"
private bool isMovingDown = false; // inital value, could be true if you want
// call this in your Update or FixedUpdate logic
private void MoveYourTowerLogic() {
// First : decide in which direction to go :
var currentAngle = RightTowers[rightIndex].transform.GetChild(2).transform.eulerAngles.z;
if (currentAngle >= 40f)
{
isMovingDown = false;
}
else if (currentAngle <= -40f)
{
isMovingDown = true;
}
// else current angle is between 40 and -40
// we simply need to o nothing, because we want to keep the previous direction set.
// remember, the variable movingDown is declared at the class level,
// so it 'remembers' the last value set from last call
// Second : Move the tower
if (isMovingDown)
{
MoveDown();
}
else // tower is moving up
{
MoveUp();
}
// NOTE : I would simply create one function Move(int angle)
// to simplify this much further and avoid to write all this mostly useless code before
// but I want to keep this simple to understand for now
}
private void MoveDown()
{
// logic to move tower down
}
private void MoveUp()
{
// logic to move tower up
}
Notice how reading this is much more "human-friendly" now, you can now, I hope, read this more easily, and that will help you modify it if you want to do more complex things later.
I strongly suggest you to avoid writing the same code several time. You can notice how I simplified and wrote only once the logic to get the angle into a variable named exactly like what I was thinking in my pseudo code.
Also, to write the methods "MoveUp" and "MoveDown", you can simply copy paste your actual code into them. But then after, you could even refactor them to avoid code repetition.
Like this :
(Edit)
Let's refactor this further
Basically, the moving logic is a completely separate task, and the only difference between moving up and down is the + or - angle.
Also, maybe you want to move your towers with a different angle later. It make sense to say that the rotation of tower depends only on the angle.
So let's create a method for that has only the angle as parameter.
With something like, instead of MoveUp() and MoveDown() :
private void MoveTower(int angle)
{
RightTowers[rightIndex].transform.GetChild(0).gameObject.GetComponent<SpriteRenderer>().sprite = Mirrors[mirrorIndex++];
rotateAngle = Quaternion.Euler(0f, 0f, RightTowers[rightIndex].transform.GetChild(2).eulerAngles.z + angle);
RightTowers[rightIndex].transform.GetChild(2).transform.rotation = rotateAngle;
}
The second part will be greatly simpler :
// Second, move the tower
MoveTower(isMovingDown ? angle : -angle);
I am trying to achieve a very basic 2D game right now where an enemy is on the screen and it bounces back and forth between set points (50 and 500) kind of like a space invaders sort of thing. My issue is I can only get it to go right, but then not come back towards the left and repeat.
I was messing around with coding it myself before bothering to look into it and actually figure it out but I thought I had something that would work, but well it doesn't, my issue is I don' get why.
My code is supposed to work like a switch, two if statements within the Update loop, one comes on the other goes off, one moves it right the other moves it left, I thought that was fine but it doesn't do that. It moves it right just fine but then the left part just doesn't work.
So, why does the following code not work?
namespace _2D_game_num1
{
class Enemy
{
int health;
Vector2 enemy_location = new Vector2(50, 50);
Vector2 enemy_speed = new Vector2(1, 1);
Player player = new Player("dummy");
public Enemy()
{
health = 100;
}
public void UpdateLocation()
{
//Vector2 player_pos = player.GetLocation();
//if (player_pos.X < 200)
// Using the players location to figure out where the enemy should move
bool right = true;
bool left = false;
if (right)
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X == 500)
{
right = false;
left = true;
}
}
if (left)
{
enemy_location.X -= enemy_speed.X;
if (enemy_location.X == 50)
{
right = true;
left = false;
}
}
}
public Vector2 GetLocation()
{
return enemy_location;
}
}
}
And then in the main Game class I have it so enemy1.UpdateLocation(); is within the Update section correctly (along with my players movement which works fine).
Try this:
public void UpdateLocation()
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X <= 50 || enemy_location.X >= 500)
{
enemy_speed.X = new Vector2(-enemy_speed.X, enemy_speed.Y);
}
}
What we are doing here is moving the enemy based on it's current speed. Then if the location is on the left or right of the screen, change direction.
Sometimes it pays to keep things simple. Get rid of the left and right flags, as they are just confusing things. When you're dealing with Update methods you're typically changing the state of something. The way you had it before, the left and right state was getting reset every time UpdateLocation is called.
Btw, you might want to consider passing in GameTime to your Update methods. Typically, when you're moving things around in real-time you'll want to multiply movement by some kind of deltaTime to keep things smooth on all devices. You're probably getting away with it because by default it'll be a fixed frame rate, but that may not always be the case, so it's a good habit to get into.
I need a little help in my little 2D game I want to create in XNA. I had almost no knowledge of programming before I got interested in XNA and C#, so maybe my problem is simple, but I just can't figure it out.
So basically, I have a base class, and I created an additional class Animation for animating sprites. I implemented some methods so that when the player presses "right" it would change the animation's current texture and increment X by a number of xf; anyway, the main idea is that I'm using just one instance of my class (basically, one object of type animation which changes its texture and properties based on what key is pressed).
So, I had no problems making it run right or left. Works out pretty well. The big problem started when I wanted to implement the jump sprite. So I created the 6 frames necessary for the sprite, but to animate it I have virtually no idea how to do it.
The only thing it does right now is to loop through the frames of the sprite, but the position (both .X and .Y) remain the same. The thing is, I have a Vector2 position which holds the animation's current position, and it's fine with running because I simply increment it. However, when it comes to jumping, I want it to increment .X, but the .Y should be decremented (thus going up) until frame number 3; after frame number 3, until the last frame, I want the .Y position to go down (thus fall) with the corresponding animations (erm, frames).
So, basically, I don't know how to modify the .X and .Y so that it would display the frames that I need in the time I need. I don't know if you really understood what I'm trying to say; basically when I press the "up" key, it loops through the frames but the position remains the same.
My idea was to use a reference to the actual Vector2 position which holds the animation's current position and pass it to the method in the other Animation.cs class, namely the PlayAnimJump, and modify the position after each frame and return it to the actual Game1.cs by reference. Even if I would do that (though I fail to see what good it would be), it wouldn't be updating the position as it should. So, any ideas?
Here is the code for the PlayAnimJump method from the Animation class:
public void PlayAnimJump(GameTime gameTime)
{
elapsed += (float)gameTime.ElapsedGameTime.Seconds;
sourceRect = new Rectangle(currentFrame * frameWidth, 0, frameWidth, frameHeight);
currentFrame = 0;
if (elapsed >= frameTime)
{
if (currentFrame <=3)
{
if (looping)
{
currentFrame++;
}
}
else if (currentFrame > 3)
{
currentFrame++;
}
elapsed = 0;
}
}
The default constructor for that class:
public Animation(ContentManager Content,string asset,float frameSpeed, int numbOfFrames, bool looping,Vector2 positionIT)
{
this.assetName = asset;
this.frameTime = frameSpeed;
this.numbOfFrames = numbOfFrames;
this.looping = looping;
this.animation = Content.Load<Texture2D>(asset);
frameWidth=(animation.Width / numbOfFrames);
frameHeight=animation.Height;
position = positionIT;
}
Here is the code (from the main) when the up key is pressed:
else if (up)
{
check = animation1.GetAsset();
if (check == "eright")
{
animation1.SetFrameSpeed(0.8f);
animation1.SetNumbOfFrames(6);
animation1.ChangeTexture(Content, "SarimCumVreJiorjica");
animation1.PlayAnimJump(gameTime);
/*position1.x +=2f;
position1.Y -=2f;
*/
}
So, I'm not sure how, but I'm supposed to change position1 according to the frame that's displayed by the animation in that second. Am I missing something?
If your animation class had a reference to the object that you wanted to move (i.e the object holding the position field) then you could modify it within the animation class, within the PlayAnimJump method.
Or, to reduce coupling, you could just have PlayAnimJump return a variable indicating how far into the jump you are (maybe a percentage of the jump, from 0 to 1). Then, you could use the percentage outside to set the objects position. So, if the jump is halfway done, the return value would be 0.5f, which you could use in an equation to determine the players y position. An example equation would be:
float percent = animation1.PlayAnimJump(gameTime);
float y = Math.Sin(percent * Math.PI) * maxJumpHeight;
player.positon.y = y;
This uses a sine wave to determine the players height throughout the jump animation. You would just need to write the code that determines the percentage of the way through the jump (currentFrame) in the PlayAnimJump method and return it.
Formula of the frуefall for Y coordinate is
y = g * t ^ 2 / 2 + v0 * t + y0
Characters jump from height y0 vith start velocity v0 by Y axis and gravity gradually slows down and starts to fall.
Calculate deltaY using following formula
deltaY = g * t ^ 2 / 2 + v0 * t
First show the frame on which the character is pushed off the ground, then the frame on which it rises until it reaches the peak of the jump. Once the sign change deltaY from + to - show how the character change pose for fall. Something like that.
How to make camera move constantly from left to right and vice versa relatively to screenCenter and without inputs, i mean automatically from the start of the game. By 'move' I mean 'watch' like as if you were turning your head to the right and to the left without changing an original position.
This is a simple example of turning script (tune it to whatever suits you best)
int timer = 0;
int direction = 1;
// Update is called once per frame
void Update () {
if (timer % 50 == 0)
direction *= -1;
transform.Rotate (new Vector3 (0, 1 * direction, 0));
timer++;
}
Also, check out Unity Answers they should be of more help