Interrupting a Jump Animation in windows form - c#

So far, I have a jump animation. It uses an integer "jump" to loop through an array of images that create a jump animation, additionally, depending on what number "jump" is,the height "PositionY" will be increased or reduced and when jump hits 10, the animation ends..... this is jump animation code
public Bitmap Jump_Frame2Draw()
{
Jump_Frames = new Bitmap[] { Jump_1, Jump_2, Jump_3, Jump_4, Jump_5, Jump_6, Jump_7,Jump_7,
Jump_8, Jump_8 };
if (jump < Jump_Frames.Length)
{
if (jump <= 3)
{
PositionY -= 30;
}
else if (jump == 8 || jump == 10)
{
PositionY += 0;
}
else
{
PositionY += 24;
}
jumpFrame = Jump_Frames[jump];
jump++;
if (jump == 10)
{
jumpTimer.Stop();
isJumping = false;
jump = 0;
standTimer.Start();
Invalidate();
}
}
tracer = jumpFrame.GetPixel(1, 1);
jumpFrame.MakeTransparent(tracer);
isJumping = true;
return jumpFrame;
}
By using a timer and a paint event, I'm able to simply call this method every x seconds inorder to draw it when I press my designated jump key... Now my problem is lets say I'm in the middle of the jump animation, and I want to go back up .How do I do I go about that. Think of it as a double jump. Also, the jump and height(PositionY) are directly correlated. So if jump is 0,1,2 or 3, then height is 214 - (jump + 1) * 30). Else if jump is (5-9) height is 94 + (jump - 4) * 24). So the maximum height that any images will ever be drawn is 94.(its windows form so up is down and down is up... (0,0) is located at top left).
///////////
For a visual perspective, this is similar to my jump animation. This one is a little shorter in terms of time but it the best I could find.
Jump animation: https://media.giphy.com/media/wXervlFEqohO0/giphy.gif
now imagine this guy was ironman, he uses his jet boosters to jump up, but now while still in the air, he decided to just go straight up.

I think you'd save yourself alot of trouble if you move most of the animation-specific code to a dedicated JumpAnimation class. Pass all information required for the specific animation in the constructor:
class JumpAnimation
{
public JumpAnimation(int howHigh)
{
...
}
}
Responding to a click on the space bar, you know you have to create a JumpAnimation. But while your timer is ticking, you don't want to deal with the specifics of a jump or jetpack animation - you want an IAnimation interface that allows you to continue the animation whatever it was. And when you acitvate the jetpack, you just want to replace whatever animation is currently active with a JetPackAnimation:
In your form:
private IAnimation currentAnimation = null;
And the IAnimation interface:
public interface IAnimation
{
// get the bitmap at the time relevant to the animation start
Bitmap GetBitmapAt(int time);
}
You can reuse alot of the code you shared in your question when you implement IAnimation in your JumpAnimation.
Now instead of returning just the Bitmap, you can create a class that holds more information about the "current step in the animation:
public class AnimationStep
{
public Bitmap Bitmap { get; set; }
// the y-offset
public int OffsetY { get; set; }
// indicates whether this was the last step of the animation
public bool Completed { get; set; }
// a jump animation can be interrupted by a jetpack animation, but a DieAnimation cant:
public bool CanBeInterrupted { get; set; }
...
}
I hope you get the idea. I'm not claiming this is the only or best way to tackle your problem, but sometimes another person's view on the matter helps thinking outside the box.

Related

Attack speed messing up animation

if (Input.GetMouseButtonDown(0))
{
if (currentanim >= 3)
{
currentanim = 1;
}
if (swordanimactive == false)
{
if (swordblockanimactive == 1)
{
currentanim = currentanim + 1;
//the problem seems to be here
Swordcontroller.speed = attackspeed;
Swordcontroller.SetInteger("attackindex", currentanim);
Swordcontroller.SetTrigger("attack");
}
}
}
The problem is when I set the attack speed over 1 (regular speed), after a few clicks it just stops working. I have no idea what causes this and I don't know how to fix it since I just implemnted this new animation system. Also, the current animation is just because there are 3 sword animations. However, if I set it to only one animation it works perfectly fine. When I use 3 different animations it doesn't work.
First of all, I do not really know how you set up the animator, but I recommend this structure.
Multi Animation Animator
Has Exit Time
Maybe you can create some dataType like
public class MeleeAttackInstance
{
public string animationName;
public float animationSpeed;
public float modifiedSpeed;
public void ApplySpeedModifier(int queCount)
{
animationSpeed*(queCount +1);
}
}
And make a queue of this attacks. When craracter should attack, you place item in queue and get the top one applying modified speed.

Automatic Fade in and out of scenes

Is there a way to activate a fade animation every time the game changes from one scene to the other, without the user doing somthing specific like hiting a pre-determined key?
Basically the transtions needs to start just because there is a change in scenes.
I have a bunch of scenes and i need to add a fade to black transition between them.
All tutorials i found need some specific key input or something happening while i only have a few videos with no possible interaction with the user (in most cases).
Just in case that's not possible (or inpractical for rookies) I guess i could hook up the animations to the user pushing buttons that send you to other scenes (could use help with that aswell). And the first scene could work with a timer/delay.
*Unity and visual studio update to latest stable builds.
So every time you need to change scene you actually need to first fade-out your screen, then run scene change and after new scene is loaded - fade screen in. You can do it like that:
1) Create new Canvas object in start scene of your project. Set canvas mode to "Screen Space - Overlay" and make sure Sort Order is set high enough so your canvas will be on top always.
new Canvas object
2) Create an empty Image on this Canvas and size it so it covers all the scree. Set Color to transparent. Dont forget to toggle "Raycast Target" of image off (or you won't be able to mouseclick through it)
new Image object
3) Add this script to your Canvas object:
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SceneChanger : MonoBehaviour
{
enum FadeStatus
{
fading_id,
fading_out,
none
}
public static SceneChanger Instance;
public Image fadeImage;
public float fadeDuration;
private FadeStatus currentFadeStatus = FadeStatus.none;
private float fadeTimer;
private string sceneToLoad;
void Start()
{
if (Instance == null)
{
Instance = this;
SceneManager.sceneLoaded += OnSceneLoaded;
}
else
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
//scene loaded, running fade-in
currentFadeStatus = FadeStatus.fading_id;
}
public void ChangeScene(string _name)
{
sceneToLoad = _name;
currentFadeStatus = FadeStatus.fading_out;
}
void Update()
{
if(currentFadeStatus != FadeStatus.none)
{
fadeTimer += Time.deltaTime;
if(fadeTimer > fadeDuration) //done fading
{
fadeTimer = 0;
if (currentFadeStatus == FadeStatus.fading_out)
{
SceneManager.LoadScene(sceneToLoad);
fadeImage.color = Color.black;
}
else
fadeImage.color = Color.clear;
currentFadeStatus = FadeStatus.none;
}
else //still fading
{
float alpha = 0;
if (currentFadeStatus == FadeStatus.fading_out)
alpha = Mathf.Lerp(0, 1, fadeTimer / fadeDuration);
else
alpha = Mathf.Lerp(1, 0, fadeTimer / fadeDuration);
fadeImage.color = new Color(0, 0,0, alpha);
}
}
}
}
4) Go back to Editor and assign your transparent Image to its field on SceneChanger script and adjust fadeDuration (time of one fade in seconds)
5) Now you can change scenes from code using
SceneChanger.Instance.ChangeScene("YourSceneName");
6) Don't forget to add all needed scenes to build settings, otherwise it won't work.
build settings
7) You DON'T need to add SceneChanger on other scenes, it will be saved between scenes due to DontDestroyOnLoad().

Weird sticky collision detection in 2D Windows Form game

This is a follow up question to a question that I posted last night. I have to write a game in a Windows Form for school, and I am creating a maze game in which a player must navigate through a maze before they are killed. As a maze game, some collision detection must be used to make sure that the player doesn't simply run through the walls (this would be quite a boring game). I've implemented a feature which prevents this based on the question that I asked last night, but I'm getting some weird results.
When the player touches a wall, the game stops them, and the player ends up getting stuck. The player CANNOT move unless they press a combination of keys to move through the wall (my game uses WASD, so if I touch a wall, I can press W + A and go through the wall to the other side where my player gets unstuck).
This is my collision code:
// This goes in the main class
foreach (Rectangle wall in mazeWalls)
{
if (playerRectangle.IntersectsWith(wall))
{
player.Stop();
}
}
This is the player's movement code:
public void Move(Direction dir)
{
// First, check & save the current position.
this.lastX = this.x;
this.lastY = this.y;
if (dir == Direction.NORTH)
{
if (!CheckCollision())
{
this.y -= moveSpeed;
}
else
{
this.y += 1;
}
}
else if (dir == Direction.SOUTH)
{
if (!CheckCollision())
{
this.y += moveSpeed;
}
else
{
this.y -= 1;
}
}
else if (dir == Direction.EAST)
{
if (!CheckCollision())
{
this.x += moveSpeed;
}
else
{
this.x -= 1;
}
}
else if (dir == Direction.WEST)
{
if (!CheckCollision())
{
this.x -= moveSpeed;
}
else
{
this.x += 1;
}
}
}
My CheckCollision() method:
private bool CheckCollision()
{
// First, check to see if the player is hitting any of the boundaries of the game.
if (this.x <= 0)
{
isColliding = true;
}
else if (this.x >= 748)
{
isColliding = true;
}
else if (this.y <= 0)
{
isColliding = true;
}
else if (this.y >= 405)
{
isColliding = true;
}
else if (isColliding)
{
isColliding = false;
}
// Second, check for wall collision.
return isColliding;
}
The Stop() method:
public void Stop()
{
this.x = lastX;
this.y = lastY;
}
Here is a gif that I have uploaded so that you can see the behavior of the player with the maze walls. Notice how he slides through the walls and repeatedly gets stuck.
My question is how do I get this player to stop sticking and actually be able to slide and move with the walls? I've tried multiple collision patterns, and used last night's (very helpful) answer, but he won't stop sticking to the walls! Let me know if you need any other details/information.
EDIT: The Input code, as requested by Dan-o: http://pastebin.com/bFpPrq7g
Game design is a complicated subject. There are some pretty well documented strategies for implementing 2D maze games. The most anyone can do here is to make general suggestions based on what you've done knowing that a commercial game is not what trying to make here. So anyhow, I'll throw in my two cents. I've only done anything like this in OpenGL, not with Windows Forms.
The first thing I see is that your sprite doesn't stay centered in it's lanes. Calculating that will ultimately make things easier because there are always only 4 possible directions that the player can be moving. North, South, East, and West don't mean as much when diagonal is also possible.
Design your gameboard so that your walls and your lanes are the same width. Use evenly spaces rows and columns. Since you're using a fixed board size, this should be as simple as dividing your width and height by the number of rows and columns you want.
Once you've done this, you will always know what lane you're in based on your x and y coordinates. If your columns are 80 pixels wide, for instance, and you're at x = 160 then you know you're in column 2 (1 in a 0-based array). This is the only way you are going to be able to put enemies on the board and be able to programmatically track where they are going. Also, you'll be able to size your players and enemies appropriately for the lanes.
Let's say your rows and columns are 80Wx60H pixels (they can be whatever you like best). Now let's say that your player is moving East starting from 0, 160. And, he can move North and South when he gets to column 3 (2 in a zero-based model), according to your map. Meaning, when he gets to 160, 160. Remember, if x = 0 starts column 1, then 80 starts column 2, and so on.
Forgetting collisions for a moment, you could use logic like this.
RectangleF playerRectangle = new RectangleF();
int COLUMN_WIDTH = 80;
int ROW_HEIGHT = 60;
if (playerRectangle.IntersectsWith(wall)){
int column = playerRectangle.X / COLUMN_WIDTH;
//----------------------------------------------
// This will return false if the player
// is not positioned right at the column. The
// result of % will contain decimal digits.
// playerRectangle.X has to be a float though.
//----------------------------------------------
if(column % 1 == 0){
//--------------------------------------------
// do this based on your keyboard logic. this
// is pseudo-code
//--------------------------------------------
if(keys == keys.UP){
// Move up
}
else if(keys == keys.DOWN){
// Move down
}
}
}
Another thing you'll want to do is to store an orientation property with your walls. That way when you collide with one, you'll know what your options are for movement. If the wall you collide with is WallOrientation.NorthSouth, you can only move North or South once you hit it. Or, you can go in reverse to the direction you hit it from. If no keys are pressed you sit still. Some thing like this will do.
public enum WallOrientation{
NorthSouth,
EastWest
}
None of this code is tested. I just wrote it on the fly so there could be mistakes. I'm just trying to give you some ideas. I hope this advice helps you out some.

How to add delay to shooting on keypress

I'm making a space shooter and I'm wondering how to make a delay with the shooting instead of spamming the space bar thanks :)
public void Shoot()
{
Bullets newBullet = new Bullets(Content.Load<Texture2D>("PlayerBullet"));
newBullet.velocity = new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation)) * 5f;
newBullet.position = playerPosition + playerVelocity + newBullet.velocity * 5;
newBullet.isVisible = true;
if (bullets.Count() >= 0)
bullets.Add(newBullet);
}
I think you want to use XNA's GameTime property. Whenever you shoot a bullet you should record the time it happened as the current GameTime.ElapsedGameTime value.
This is a TimeSpan so you could compare it like the following:
// Create a readonly variable which specifies how often the user can shoot. Could be moved to a game settings area
private static readonly TimeSpan ShootInterval = TimeSpan.FromSeconds(1);
// Keep track of when the user last shot. Or null if they have never shot in the current session.
private TimeSpan? lastBulletShot;
protected override void Update(GameTime gameTime)
{
if (???) // input logic for detecting the spacebar goes here
{
// if we got here it means the user is trying to shoot
if (lastBulletShot == null || gameTime.ElapsedGameTime - (TimeSpan)lastBulletShot >= ShootInterval)
{
// Allow the user to shoot because he either has not shot before or it's been 1 second since the last shot.
Shoot();
}
}
}
You can have a counter that counts the number of frames since the last shot.
int shootCounter = 0;
...
if(shootCounter > 15)
{
Shoot();
shootcounter = 0;
}
else
{
shootcounter++;
}
If you want to go more advanced, you can use the gameTime that the default update method normally gives you to keep track of how much times has passed since you've last shot.

Make a basic running sprite effect

I'm building my very first game with XNA and i'm trying to get my sprite to run.
Everything is working fine for the first sprite.
E.g : if I go right(D) my sprite is looking right , if I go left(A) my sprite is looking left and if I don't touch anything my sprite is the default one.
Now what I want to do is if the sprite goes Right, i want to alternatively change sprites (left leg, right leg, left leg etc..) xCurrent is the current sprite drawn xRunRight is the first running Sprite and xRunRight1 is the one that have to exchange with xRunRight while running right.
This is what I have now :
protected override void Update(GameTime gameTime)
{
float timer = 0f;
float interval = 50f;
bool frame1 = false ;
bool frame2 = false;
bool running = false;
KeyboardState FaKeyboard = Keyboard.GetState();
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if ((FaKeyboard.IsKeyUp(Keys.A)) || (FaKeyboard.IsKeyUp(Keys.D)))
{
xCurrent = xDefault;
}
if (FaKeyboard.IsKeyDown(Keys.D))
{
timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (timer > interval)
{
if (frame1)
{
xCurrent = xRunRight;
frame1 = false;
}
else
{
xCurrent = xRunRight1;
frame1 = true;
}
}
xPosition += xDeplacement;
}
Any ideas...? I've been stuck on this for a while..
Thanks in advance and let me know if you need any other part from the code.
You forgot to reset the timer, you could do this when you hit the timer interval condition. Also, 50ms seems a bit too small for an interval, maybe you could do like 400ms?
Other than that, looks good, it will do what you want.
Alternatively, you could look into making animated sprites for walking. What you do is make an image with the walking animation sprites one next to the other, of the same size. You draw only a portion of this image (one sprite) and move through them based on time.
Here is a quick code for what could be an animated texture:
class AnimatedTexture2D : Texture2D
{
int _numberOfImages;
int _currentImage = 0;
int _timeInterval;
int _spriteWidth;
public Rectangle DrawFromRectangle
{
get
{
return new Rectangle(_currentImage * _spriteWidth, 0, _spriteWidth, this.Height);
}
}
public AnimatedTexture2D(Texture2D entireImage, int spriteWidth, int numberOfImages, int timeInterval)
: base(entireImage.GraphicsDevice, entireImage.Width, entireImage.Height)
{
_numberOfImages = numberOfImages;
_timeInterval = timeInterval;
_spriteWidth = spriteWidth;
Color[] data = new Color[entireImage.Width * entireImage.Height];
entireImage.GetData<Color>(0, new Rectangle(0, 0, entireImage.Width, entireImage.Height), data, 0, entireImage.Width * entireImage.Height);
this.SetData<Color>(data);
}
public void Animate(GameTime gameTime)
{
int totalImageTime = _timeInterval * _numberOfImages;
int currentPoint = (int)gameTime.TotalGameTime.TotalMilliseconds % totalImageTime;
_currentImage = currentPoint / _timeInterval;
}
}
Usage is fairly simple:
1) declare it somewhere:
AnimatedTexture2D animatedTexture;
2) initiate it with your texture (i had a 2560x64 sequence of 40 64*64 images), where individual images are placed next to each other horizontally:
animatedTexture = new AnimatedTexture2D(Content.Load<Texture2D>(#"Textures\Loading"), 64, 40, 20);
3) in your Update() method, call:
animatedTexture.Animate(gameTime);
4) in your Draw() method, call:
SpriteBatch.Draw(animatedTexture, new Rectangle(20, 20, 64, 64), animatedTexture.DrawFromRectangle, Color.White);
Don't forget the DrawFromRectangle in part 4! (notice that the destination rectangle uses the declared individual part width, not the entire texture width which is in my test 2560 pixels)
Now, in your code you could forget the interval part, and the gametime part, and you could just use this instead of the default one!
Also, if you don't like my timing code (its ultra simple but lacks a way to reset the animation) change it so you have an elapsed time variable, and add to it like you do in your own code, and use that to change _currentImage. You could even make that variable public so you can use it to reset the animation (or set it to a specified point).
Of course, you could also make the default one an animated texture with only one frame so you can use the same code everywhere. Hope this helps!
You need to keep the last time Update(..) was called, and the interval should be... well.. an interval, that is, the difference between ElapsedGameTime and last call to update ElapsedGameTime.
Do it with a new member of your class (LastElapsedGameTimeUpdateCalled) or a static member of your sub.
If you know that every animation is going to have the same number of frames you could keep 3 variables per sprite (encapsulate in class for best results).
BaseFrame is an integer to hold a global animation number.
SubFrame is an offset in to the animation that holds which frame you are currently on.
FrameAccumulator to hold timing information.
Each time update is called, add the number of ms since the last update to the accumulator. Once the accumulator goes above your animation timing, increment SubFrame, and reset the accumulator. Check to see if subFrame is greater than the number of frames for each animation, and if it is, set it back to 0. You can get the real frame index from this by adding BaseFrame + Subframe. When you need to display a different animation just change the BaseFrame.
Lets say that each animation has 3 frames, and you have 2 total animations. You would have 6 total frames. RunLeft would be BaseFrame 0, and RunRight would be BaseFrame 3. That should easily give you the frame number to draw.

Categories

Resources