I am trying to create kind of a spaceship game, where you control an image (of a spaceship obviously) with the arrow keys, to move UP/DOWN/R/L.
For this I use a CoreWindow.KeyDown event.
It actually works fine, but the motion is not smooth enough.
Every time I press one of the arrow keys, the image then:
1. Moves one step to that direction
2. FREEZES, for like half a second (even less)
3. Then continue on moving with no trouble.
(A "step", is an 'int' variable the holds a number of pixels, say, 20).
Of course, that is no way to run a game. I want the ship to move smoothly immediately when i press one of the arrow keys with no "Half a second" delay.
This is my code:
public sealed partial class MainPage : Page
{
double playerYAxisPosition;
double playerXAxisPosition;
int steps = 20;
bool upMovement;
bool downMovement;
bool rightMovement;
bool leftMovement;
public MainPage()
{
this.InitializeComponent();
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
Window.Current.CoreWindow.KeyUp += CoreWindow_KeyUp;
}
// Recognizes the KeyDown press and sets the relevant booleans to "true"
private void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args) {
playerYPosition = (double) playerShip.GetValue(Canvas.TopProperty);
playerXPosition = (double) playerShip.GetValue(Canvas.LeftProperty);
if (args.VirtualKey == Windows.System.VirtualKey.Up) {
upMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Down) {
downMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Left) {
leftMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Right) {
rightMovement = true;
}
movePlayer();
}
// recognizes the KeyUp event and sets the relevant booleans to "false"
private void CoreWindow_KeyUp(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args) {
if (args.VirtualKey == Windows.System.VirtualKey.Up) {
upMovement = false;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Down) {
downMovement = false;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Left) {
leftMovement = false;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Right) {
rightMovement = false;
}
}
// Calls the movement Methods of the relevant direction
private void movePlayer() {
if (upMovement) {
moveUp();
}
if (downMovement) {
moveDown();
}
if (rightMovement) {
moveRight();
}
if (leftMovement) {
moveLeft();
}
}
private void moveUp() {
playerShip.SetValue(Canvas.TopProperty, playerYPosition - stepsToMove);
}
private void moveDown() {
playerShip.SetValue(Canvas.TopProperty, playerYPosition + stepsToMove);
}
private void moveRight() {
playerShip.SetValue(Canvas.LeftProperty, playerXPosition + stepsToMove);
}
private void moveLeft() {
playerShip.SetValue(Canvas.LeftProperty, playerXPosition - stepsToMove);
}
By the way, the reason I created some dedicated booleans that will be set to 'true' or 'false' on every KeyDown event, and not used the KeyDown event directly, is because that separation allows the element (the ship image) to move diagonally as well, while using the "args.VirtualKey == Windows.System.VirtualKey.SomeArrowKey" directly does not allow it from some reason.
Thank's for your help.
If you want create a game, you should move playerShip in your game loop. You can use DispatcherTimer or register CompositionTarget.Rendering event to create a game loop.
Windows.UI.Xaml.Media.CompositionTarget.Rendering += CompositionTarget_Rendering;
Change CoreWindow_KeyDown event handler as follows:
private void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
{
if (args.VirtualKey == Windows.System.VirtualKey.Up)
{
upMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Down)
{
downMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Left)
{
leftMovement = true;
}
else if (args.VirtualKey == Windows.System.VirtualKey.Right)
{
rightMovement = true;
}
}
Next, move your ship in CompositionTarget.Rendering event handler.
private void CompositionTarget_Rendering(object sender, object e)
{
playerYPosition = (double)playerShip.GetValue(Canvas.TopProperty);
playerXPosition = (double)playerShip.GetValue(Canvas.LeftProperty);
movePlayer();
}
Related
I tried two different way to collect coins and both didn't work. I don't know what I'm doing wrong. I'm coding in c#. Is there something I'm missing or is there something I'm supposed to do beforehand?
Here is method number 1:
//collecting coins
foreach(Control x in this.Controls)
{
if (x is PictureBox && x.Tag == "coin")
{
if (((PictureBox)x).Bounds.IntersectsWith(pacman.Bounds))
{
this.Controls.Remove(x); //remove that point
score++; // add to the score
}
}
}
and Here is method number 2:
//collecting coins
foreach(Control x in this.Controls)
{
if (x is PictureBox)
{
if ((string)x.Tag == "coin" && x.Visible == true)
{
if (pacman.Bounds.IntersectsWith(x.Bounds))
{
score += 1;
x.Visible = false;
}
}
}
}
This is work in progress so the code is still missing some parts. Here is a bit of the source code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Pac_Man_Game_Clone
{
public partial class Form1 : Form
{
bool goup, godown, goright, goleft, isGameOver;
int score, playerSpeed, redGhostSpeed, yellowGhostSpeed, pinkGhostX, pinkGhostY;
public Form1()
{
InitializeComponent();
restGame();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void keyisdown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Up)
{
goup= true;
}
if(e.KeyCode == Keys.Down)
{
godown= true;
}
if(e.KeyCode == Keys.Left)
{
goleft= true;
}
if(e.KeyCode == Keys.Right)
{
goright= true;
}
}
private void keyisup(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
goup = false;
}
if (e.KeyCode == Keys.Down)
{
godown = false;
}
if (e.KeyCode == Keys.Left)
{
goleft = false;
}
if (e.KeyCode == Keys.Right)
{
goright = false;
}
}
private void mainGameTimer(object sender, EventArgs e)
{
txtScore.Text = "Score: " + score;
//moving pacman
if(goleft == true)
{
pacman.Left -= playerSpeed;
pacman.Image = Properties.Resources.left;
}
if(goright == true)
{
pacman.Left += playerSpeed;
pacman.Image = Properties.Resources.right;
}
if(godown == true)
{
pacman.Top += playerSpeed;
pacman.Image = Properties.Resources.down;
}
if(goup == true)
{
pacman.Top -= playerSpeed;
pacman.Image = Properties.Resources.Up;
}
//leaving the parameters
//left and right
if (pacman.Left < -10)
{
pacman.Left = 680;
}
if (pacman.Left > 680)
{
pacman.Left = -10;
}
//up and down
if (pacman.Top < -10)
{
pacman.Top = 550;
}
if (pacman.Top > 550)
{
pacman.Top = -10;
}
//collecting coins
foreach(Control x in this.Controls)
{
if (x is PictureBox)
{
if ((string)x.Tag == "coin" && x.Visible == true)
{
if (pacman.Bounds.IntersectsWith(x.Bounds))
{
score += 1;
x.Visible = false;
}
}
}
//runs through wall
if ((string)x.Tag == "wall")
{
if (pacman.Bounds.IntersectsWith(x.Bounds))
{
// run game over
}
}
//bumps to ghost
if ((string)x.Tag == "ghost")
{
if (pacman.Bounds.IntersectsWith(x.Bounds))
{
// run game over
}
}
}
//moving ghosts
redGhost.Left += redGhostSpeed;
if (redGhost.Bounds.IntersectsWith(pictureBox1.Bounds || redGhost.Bounds.IntersectsWith(pictureBox2.Bounds))
{
redGhost += redGhostSpeed;
}
if (score == int.MaxValue)
{
//run game over
}
}
private void restGame()
{
txtScore.Text = "Score: 0";
score= 0;
redGhostSpeed = 5;
yellowGhostSpeed = 5;
pinkGhostX = 5;
pinkGhostY = 5;
playerSpeed = 8;
isGameOver= false;
pacman.Left = 7;
pacman.Top= 35;
redGhost.Left = 219;
redGhost.Top = 56;
yellowGhost.Left = 450;
yellowGhost.Top = 447;
pinkGhost.Left = 521;
pinkGhost.Top = 227;
foreach(Control x in this.Controls)
{
if(x is PictureBox)
{
x.Visible= true;
}
}
gameTimer.Start();
}
private void gameOver(string message)
{
}
}
}
Without having the full source code and the visual tree, I can only try to understand why this isn't working. To me looks like the pacman object isn't defined in the same parent as the coins.
The bounds property is relative to the parent control as coordinate origin.
It works well if each element in the collection of controls are in at the same level of the visual tree, with the same parent. If not, intersect will hardly be true because it is checking against two different coordinate origins, and even if returns true, the position on screen could not match.
As your pacman seems to be referred somewhere else than your collection, maybe your control does not have the same parent and will unlikely have the same origin for your pacman and the coin, and the IF...ELSE will never hit. Please be sure to use the same parent for the pacman and the coins.
Another solution is to draw the game objects on a canvas using the drawing primitives, and create ad hoc objects to store the drawing rectangles. This is the preferred solution as using GDI+ objects to draw game elements doesn't perform well. If you use the canvas instead, all you must do is to update the canvas on each drawing loop with the images of your game objects. This way, you will have no issues with the visual tree of the controls.
Edit: as stated in other comments, if you succeed to hit the intersection condition, in the first version of your code you will also face exception for removing items from the collection while iterating it using foreach. In this case, the solution is to iterate the list first and add the items to a new "removedItemsList" and then iterate this new list to remove the items from the controls collection without iterate it.
It's not so efficient, your second solution with visibility in my opinion is far better. It's not efficient for at least two motives: one because every time you remove a control the visual tree updated for each remaining control. Second, you must allocate a new list, and reiterate it every time you hit a coin. Visibility instead only invalidate the control render rectangle, and on the next draw routing it will be draw invisible.
Currently, this code runs fine, and moves the picturebox, but it always moves it once, waits about a second, and then continues to move it. How do I make the movement keep going instead of stopping for a second?
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
{
x = pictureBox1.Location.X;
y = pictureBox1.Location.Y;
if (pictureBox1.Location.X > 0)
{
pictureBox1.Location = new Point(x - 10, y);
}
}
}
One way to achieve this outcome is to use a Timer and enable it on KeyDown event and disable it on KeyUp event. When MoveTimer.Tick occurs (in this case every 25 ms or so), handle the event by moving "once" to the left until the key is released.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
MoveTimer.Tick += (sender, e) =>
{
pictureBox1.Location = new Point(
pictureBox1.Location.X - 10,
pictureBox1.Location.Y);
};
}
Timer MoveTimer = new Timer { Interval = 25 };
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if(e.KeyData == Keys.A)
{
MoveTimer.Enabled = true;
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
MoveTimer.Enabled = false; // No need to check which key
}
}
Basically,I am a beginner in C# and I just started working on a simple platformer game following a basic Visual Studio 2015 tutorial.This is the code:
public partial class Form1 : Form
{
bool goleft = false;
bool goright = false;
bool jumping = false;
int JumpSpeed = 10;
int force = 8;
int score = 0;
public Form1()
{
InitializeComponent();
}
private void keyisdown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
goleft = true;
}
if (e.KeyCode == Keys.Right)
{
goright = true;
}
if (e.KeyCode == Keys.Space && !jumping)
{
jumping = true;
}
}
private void keyisup(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
goleft = false;
}
if (e.KeyCode == Keys.Right)
{
goright = false;
}
if (jumping)
{
jumping = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
player.Top += JumpSpeed;
if (jumping && force < 0)
{
jumping = false;
}
if (goleft)
{
player.Left -= 5;
}
if (goright)
{
player.Left += 5;
}
if (jumping)
{
JumpSpeed = -12;
force -= 1;
}
else
{
JumpSpeed = 12;
}
foreach (Control x in this.Controls)
{
if (x is PictureBox && x.Tag == "p")
{
if (player.Bounds.IntersectsWith(x.Bounds) && !jumping)
{
force = 8;
player.Top = x.Top - player.Height;
}
}
if (player.Bounds.IntersectsWith(Door.Bounds))
{
timer1.Stop();
MessageBox.Show("You Won!");
this.Close();
}
}
}
}
The gravity force and everything works great EXCEPT for one big problem with the player that I definitely can't solve without some help.
The problem is: the player is completely glitched and it keeps going up and down. I realised that this is because of two lines in private void timer1_Tick(object sender, EventArgs e):
player.Top += JumpSpeed;
and
player.Top = x.Top - player.Height;
Both of these try to change player.Top and interfere with each other. The trouble is that I can't just cut one of the two without destroying the gravity force code. What I think might work is applying player.Top += JumpSpeed; ONLY if(player.Bounds.IntersectsWith(x.Bounds) && !jumping) = false but I don't know how to write the code this way at all (previous tries always ended up with errors).
Thanks in advance for any help and please forgive any grammar errors that I made because I am not a native english speaker.
I don't know how your Form looks like, but with what you provided I would change the code like that:
public partial class Form1 : Form
{
bool goleft = false;
bool goright = false;
int JumpSpeed = 0; //current vertical speed
int force = 8; //initial jump speed
int score = 0;
public Form1()
{
InitializeComponent();
}
private void keyisdown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
goleft = true;
}
if (e.KeyCode == Keys.Right)
{
goright = true;
}
if (e.KeyCode == Keys.Space && JumpSpeed==0) //JumpSpeed==0 to prevent "double jump"
{
JumpSpeed = force; //start jumping with full power
}
}
private void keyisup(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Left)
{
goleft = false;
}
if (e.KeyCode == Keys.Right)
{
goright = false;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (goleft)
{
player.Left -= 5;
}
if (goright)
{
player.Left += 5;
}
JumpSpeed -= 1; //decrement the vertical speed = gravity -> choose higher value to bring him back on the ground quicker
player.Top -= JumpSpeed;
if (player.Bounds.IntersectsWith(Door.Bounds))
{
timer1.Stop();
MessageBox.Show("You Won!");
this.Close();
}
foreach (PictureBox x in this.Controls.OfType<PictureBox>())
{
if (x.Tag == "p")
{
if (player.Bounds.IntersectsWith(x.Bounds)) //if the player should be able to jump/walk on something the code is
{
player.Top = x.Top - player.Height;
JumpSpeed = 0; //the player stops falling
}
}
}
}
}
To make sure to player does not fall out of the window place PictureBoxes with tag == p on the floor!
PS:
Feel free to leave a comment!
I have a button in ListView which shows and hides based on the button press in ActionBar.
The button is in GetView function like this
ImageButton btnDel = view.FindViewById<ImageButton>(Resource.Id.btn_row_del);
if(_isEdit)
{
if(btnDel.Visibility == ViewStates.Gone)
{
btnDel.Animate().TranslationX(0).SetDuration(2000);
}
}
else
{
if (btnDel.Visibility == ViewStates.Visible)
{
btnDel.Animate().TranslationX(btnDel.Width).SetDuration(2000);
}
}
But the animation doesn't seem to be working.
How can I animate a button inside of a list view.
You might set the button gone or visible immediately when you press your action bar.
You need to set the button gone or visible when animation end by adding the following call back listener:
private void Bt2_Click(object sender, System.EventArgs e)
{
if (bt1.Visibility == ViewStates.Visible)
{
AlphaAnimation disappearAnimation = new AlphaAnimation(1, 0);
disappearAnimation.Duration = 2000;
bt1.StartAnimation(disappearAnimation);
disappearAnimation.AnimationStart += DisappearAnimation_AnimationStart;
disappearAnimation.AnimationEnd += DisappearAnimation_AnimationEnd;
}
else
{
AlphaAnimation disappearAnimation = new AlphaAnimation(0, 1);
disappearAnimation.Duration = 2000;
bt1.StartAnimation(disappearAnimation);
disappearAnimation.AnimationStart += DisappearAnimation_AnimationStart;
disappearAnimation.AnimationEnd += DisappearAnimation_AnimationEnd;
}
}
private void DisappearAnimation_AnimationStart(object sender, Animation.AnimationStartEventArgs e)
{
if (bt1.Visibility == ViewStates.Visible)
{
bt1.Animate().TranslationX(bt1.Width).SetDuration(2000);
}
else
{
bt1.Animate().TranslationX(0).SetDuration(2000);
}
}
private void DisappearAnimation_AnimationEnd(object sender, Animation.AnimationEndEventArgs e)
{
if (bt1.Visibility == ViewStates.Visible)
{
bt1.Visibility = ViewStates.Invisible;
}
else
{
bt1.Visibility = ViewStates.Visible;
}
}
screen shot:
Add a Start() to your animation:
btnDel.Animate().TranslationX(btnDel.Width).SetDuration(2000).Start();
When I hold a key in my game to move my player:
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
Player.MoveUp();
}
}
The player instantly moves one step as soon as I press the down arrow, and then pauses for a short duration before starting to smoothly move again. Why's this? How can I prevent it?
The answer in the proposed duplicate is incorrect, unfortunately. It doesn't ignore repeated KeyDown events, and so will gradually increase the "delta" value in the direction being handled by each key case. It also doesn't respond to the keypress immediately (i.e. no action happens until the first timer tick).
This answer to Holding Arrow Keys Down For Character Movement C# .Net ISSUES explains how to ignore the subsequent KeyDown events, but doesn't explain how then your character would move.
In other words, I couldn't find a duplicate question that actually correctly answers your question. So…
The basic technique you want to do is:
Don't move on the actual key input. Instead, generate your own timing logic that will move the object.
Instead of using the KeyDown event to actually move the object, use it to set a movement direction, which is then processed by your timing logic.
There are a variety of ways to accomplish this. One version would look like this:
private bool _moveUp;
private bool _moveDown;
private bool _moveLeft;
private bool _moveRight;
// You can add the Timer in the Winforms Designer instead if you like;
// The Interval property can be configured there at the same time, along
// with the Tick event handler, simplifying the non-Designer code here.
private System.Windows.Forms.Timer _movementTimer = new Timer { Interval = 100 };
public MainForm()
{
InitializeComponent();
_movementTimer.Tick += movementTimer_Tick;
}
private void movementTimer_Tick(object sender, EventArgs e)
{
_DoMovement();
}
private void _DoMovement()
{
if (_moveLeft) Player.MoveLeft();
if (_moveRight) Player.MoveRight();
if (_moveUp) Player.MoveUp();
if (_moveDown) Player.MoveDown();
}
// You could of course override the OnKeyDown() method instead,
// assuming the handler is in the Form subclass generating the
// the event.
public void MainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
// Ignore key repeats...let the timer handle that
return;
}
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = true;
break;
case Keys.Down:
_moveDown = true;
break;
case Keys.Left:
_moveLeft = true;
break;
case Keys.Right:
_moveRight = true;
break;
}
_DoMovement();
_movementTimer.Start();
}
public void MainForm_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
_moveUp = false;
break;
case Keys.Down:
_moveDown = false;
break;
case Keys.Left:
_moveLeft = false;
break;
case Keys.Right:
_moveRight = false;
break;
}
if (!(_moveUp || _moveDown || _moveLeft || _moveRight))
{
_movementTimer.Stop();
}
}
Do note that the timer objects in .NET have limited resolution. I show an interval of 100 ms (10 times per second) above (same as in the other question's answer), and this is about as frequent an update as you're going to reliably get. Even then, the timer's Tick event may not (and probably won't) be raised on exactly 100 ms intervals. There will be some variation back and forth. But it will be close enough for a basic game.
If you need more precision than that, you will have to implement your own state-polling-and-animation loop somewhere. That's a whole other ball o' wax. :)
A subjectivly elegent approach:
public partial class Form1 : Form
{
private static Timer timer;
private static bool[] keys_down;
private static Keys[] key_props;
private void Form1_Load(object sender, EventArgs e)
{
keys_down = new bool[4];
key_props = new []{Keys.A, Keys.D, Keys.W, Keys.S};
timer = new Timer();
timer.Interval = 15; // Roughly 67 FPS
timer.Tick += tick;
timer.Start();
KeyDown += key_down_event;
KeyUp += key_up_event;
... // More things to do when the form loads.
}
private void tick(Object source, EventArgs e)
{
... // Do this every timing interval.
byte n = 0;
foreach (var v in keys_down)
{
if (n == 3 && v)
... // If the "s" key is being held down, no key delay issues. :)
n++;
}
...
}
private void key_down_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = true;
n++;
}
}
private void key_up_event(object sender, KeyEventArgs e)
{
byte n = 0;
foreach (var v in keys_down)
{
if (e.KeyCode == key_props[n])
keys_down[n] = false;
n++;
}
}
public Form1()
{
InitializeComponent();
}
}
I was looking for solutions to create a small copy of Flappy Bird. Perhaps my decision will help someone.
I used the addition of a player position with a variable in timer to simulate gravity. When I press W, I turned off the further reception of the command using bool and simply inverted gravity
private void time_Tick(object sender, EventArgs e)
{
if (bird.Location.Y >= 540)
{
bird.Location = new Point(bird.Location.X, 540);
}
else
{
bird.Location = new Point(bird.Location.X, bird.Location.Y+grav);
}
}
private void Form1_KeyPress(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == false)
{
press = true;
grav = -10;
}
return;
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.W && press == true)
{
press = false;
grav = 5;
}
return;
}