I am learning C #, and I am creating a hypothetical game for me to understand the language. I want several bots to follow the Player who is moving the rectangle, but I can only move the player, but the automatic bots do not move.
I really researched what I could do to move these bots. And I came to the conclusion that I would have to understand Threads, which simply causes the program not to crash.
I leave here the full code of what I am trying.
public partial class Form1 : Form
{
public enum Direction { Up, Down, Left, Right }
private Player player;
private List<Bot> bots;
public Form1()
{
InitializeComponent();
this.Paint += Form1_Paint;
this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
this.player = new Player(new Size(8, 8));
this.bots = new List<Bot>();
for (int i = 0; i < 30; i++)
{
Bot bot = new Bot(player, new Size(8, 8));
bot.Follow();
this.bots.Add(bot);
}
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
player.Move(Direction.Up);
break;
case Keys.Down:
player.Move(Direction.Down);
break;
case Keys.Left:
player.Move(Direction.Left);
break;
case Keys.Right:
player.Move(Direction.Right);
break;
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
List<Rectangle> rs = new List<Rectangle>();
rs = this.bots.Select(x => x.Rectangle).ToList();
rs.Add(player.Rectangle);
if (rs.Count > 0)
{
e.Graphics.FillRectangles(new SolidBrush(Color.Red), rs.ToArray());
}
}
}
public class Player
{
private Rectangle rectangle;
public Rectangle Rectangle { get => rectangle; }
public Player(Size size)
{
this.rectangle = new Rectangle() { Size = size };
}
public void Move(Direction direction)
{
switch (direction)
{
case Direction.Up:
this.rectangle.Y -= 3;
break;
case Direction.Down:
this.rectangle.Y += 3;
break;
case Direction.Left:
this.rectangle.X -= 3;
break;
case Direction.Right:
this.rectangle.X += 3;
break;
default:
break;
}
}
}
public class Bot
{
private Rectangle rectangle;
private Player player;
public Rectangle Rectangle { get => rectangle; }
public Bot(Player player, Size size)
{
this.player = player;
this.rectangle = new Rectangle() { Size = size };
}
public void Follow()
{
Task.Run(() =>
{
while (true)
{
Point p = player.Rectangle.Location;
Point bot = rectangle.Location;
for (int i = bot.X; i < p.X; i += 2)
{
Thread.Sleep(100);
bot.X = i;
}
for (int i = bot.X; i > p.X; i -= 2)
{
Thread.Sleep(100);
bot.X = i;
}
for (int i = bot.Y; i < p.Y; i += 2)
{
Thread.Sleep(100);
bot.Y = i;
}
for (int i = bot.Y; i > p.Y; i -= 2)
{
Thread.Sleep(100);
bot.Y = i;
}
}
});
}
}
As you can see, I can only move the player, but the bots don't move what can I do to move the bots?
I think a Timer would work better here and remove the requirement for you to fully understanding threading at this point, as it will handle the details for you. I'm assuming you actually want the bots to "follow" instead of only moving when the Player moves and will fall behind if the player is moving quickly.
So to use a Timer, I would adjust your Bot class as below, to remove your usage of threads and only allow it to take a single step towards the player in the Follow method which will be called every 100ms. Note Rectangle is a struct, so it is not mutable - that is why your bots do not move - if you do the following:
Point bot = Rectangle.Location;
bot.X = 5;
You're probably thinking Rectangle.Location.X is now 5; but it is not. So we create a new rectangle using the new position.
public class Bot
{
private Player player;
public Rectangle Rectangle { get; set; }
public Bot(Player player, Size size)
{
this.player = player;
this.Rectangle = new Rectangle() { Size = size };
}
public void Follow()
{
Point p = player.Rectangle.Location;
Point bot = Rectangle.Location;
for (int i = bot.X + 2; i < p.X;)
{
bot.X = i;
break;
}
for (int i = bot.X - 2; i > p.X;)
{
bot.X = i;
break;
}
for (int i = bot.Y + 2; i < p.Y;)
{
bot.Y = i;
break;
}
for (int i = bot.Y - 2; i > p.Y;)
{
bot.Y = i;
break;
}
Rectangle = new Rectangle(bot, player.Rectangle.Size);
}
}
And add the following code to replace your existing constructor and add another method to handle the Timer tick.
private Timer timer;
public Form1()
{
InitializeComponent();
this.Paint += Form1_Paint;
this.KeyPreview = true;
this.KeyDown += Form1_KeyDown;
// setup a timer which will call Timer_Tick every 100ms
timer = new System.Windows.Forms.Timer();
timer.Interval = 100;
timer.Tick += Timer_Tick;
timer.Start();
this.player = new Player(new Size(8, 8));
this.bots = new List<Bot>();
for (int i = 0; i < 30; i++)
{
Bot bot = new Bot(player, new Size(8, 8));
bot.Follow();
this.bots.Add(bot);
}
}
private void Timer_Tick(object sender, System.EventArgs e)
{
foreach (var bot in bots)
bot.Follow();
this.Invalidate();
}
Point is a value type (a struct). (Read more about this at What's the difference between struct and class in .NET?)
When you did this:
Point bot = Rectangle.Location;
bot.X = i;
...you created a local Point and modified it. This doesn't change the Location of the Rectangle of the Bot. Also, Rectangles are structs too, so you have to modify the original Bot's Rectangle, or assign a new Rectangle to the Bot.
To fix, you could replace:
bot.X = i;
...with...
this.rectangle.X = i;
And make the similar change for .Y (in all your for loop locations)
Spelling it all out:
public void Follow()
{
Task.Run(() =>
{
while (true) {
Point p = player.Rectangle.Location;
Point bot = rectangle.Location;
for (int i = bot.X; i < p.X; i += 2) {
Thread.Sleep(100);
this.rectangle.X = i;
}
for (int i = bot.X; i > p.X; i -= 2) {
Thread.Sleep(100);
this.rectangle.X = i;
}
for (int i = bot.Y; i < p.Y; i += 2) {
Thread.Sleep(100);
this.rectangle.Y = i;
}
for (int i = bot.Y; i > p.Y; i -= 2) {
Thread.Sleep(100);
this.rectangle.Y = i;
}
}
});
}
Related
Now I am reading a book C # Programming for the Absolute Beginner
In Chapter 7, Section workouts, which put it.My question is that
The Interval property of a timer is specified in milliseconds, not ticks.
Now, how can I change the value of a variable(dy) based on each tick
Modify the difficulty level of the Lunar Lander game. There are several ways you could
tweak the code. Perhaps you could change gravity by modifying the change in dy during
each tick of the timer. You could also adjust how much dx and dy change during each key
press or how fast time progresses by modifying the timer’s interval. Another easy change
would be to modify the size of the landing pad or the lander.
public partial class theForm : Form
{
private double x, y; //will show new position of lander
private double dx, dy; //diffrence in x and y
private int fuel = 100; //how much fuel is left
private int ships = 3; // number of ships player has
private int score = 0; //the player's current score
private int level = 1;
public theForm()
{
InitializeComponent();
initGame();
}
private void timer1_Tick(object sender, EventArgs e)
{
//account for gravity
dy += 0.5;
//increment score for being alive
score += 100;
//show ordinary (no flames) lander
picLander.Image = myPics.Images[0];
//call helper methods to handle details
moveShip();
checkLanding();
showStats();
}//end timer tick
private void showStats()
{
//display the statistics
lblDx.Text = "dx: " + dx;
lblDy.Text = "dy: " + dy;
lblFuel.Text = "fuel: " + fuel;
lblShips.Text = "ships: " + ships;
lblScore.Text = "score: " + score;
}//end showStats
private void checkLanding()
{
//get rectangle from the objects
Rectangle rLander = picLander.Bounds;
Rectangle rPlatform = picPlatform.Bounds;
//look for an interesection
if (rLander.IntersectsWith(rPlatform))
{
//it's either a crash or a landing
//turn off the timer for a moment
timer1.Enabled = false;
if (Math.Abs(dx) < 3)
{
//horizontal speed ok
if (Math.Abs(dy) < 5)
{
//vertical speed ok
if (Math.Abs(rLander.Bottom - rPlatform.Top) < 3)
{
//landing on top of platform
MessageBox.Show("Good Landing!");
lblLevel.Text=Convert.ToString(level++);
fuel += 30;
score += 1000;
}
else
{
//not on top of platform
MessageBox.Show("You have to land on top.");
killShip();
}//end vertical if
}
else
{
//dy too large
MessageBox.Show("Too much vertical velocity!");
killShip();
} // end vertical if
}
else
{
//dx too large
MessageBox.Show("too much horizontal velocity");
killShip();
}//end horiz if
//reset the lander
initGame();
}//end if
}//end checkLanding
private void initGame()
{
//re-initializes the game
Random roller = new Random();
int platX, platY;
//find random dx,dy values for lander
dx = Convert.ToDouble(roller.Next(5) - 2);
dy = Convert.ToDouble(roller.Next(5) - 2);
//place lander randomly on form
x = Convert.ToDouble(roller.Next(this.ClientSize.Width));
y = Convert.ToDouble(roller.Next(this.ClientSize.Height));
//place platform randomly on form (but make sure it appears)
platX = roller.Next(this.ClientSize.Width - picPlatform.Width);
platY = roller.Next(this.ClientSize.Height - picPlatform.Height);
picPlatform.Location = new Point(platX, platY);
//turn on timer
timer1.Enabled = true;
}//end initGame
private void killShip()
{
//kill off a ship,check for end of game
DialogResult answer;
ships--;
if (ships <= 0)
{
//game is over
answer = MessageBox.Show("Play Again?", "Game Over", MessageBoxButtons.YesNo);
if (answer == DialogResult.Yes)
{
ships = 3;
fuel = 100;
initGame();
}
else
{
Application.Exit();
}//end if
}//end 'no ships' if
}//end killShip
private void theForm_KeyDown(object sender, KeyEventArgs e)
{
fuel--;
//check to see if user is out of gas
if (fuel < 0)
{
timer1.Enabled = false;
MessageBox.Show("Out of Fuel!!!");
fuel += 20;
killShip();
initGame();
}//end if
//look for arrow keys
switch (e.KeyData)
{
case Keys.Up:
picLander.Image = myPics.Images[1];
dy -= 2;
break;
case Keys.Left:
picLander.Image = myPics.Images[2];
dx++;
break;
case Keys.Right:
picLander.Image = myPics.Images[3];
dx--;
break;
default:
//do nothing
break;
}//end switch
if(e.KeyCode==Keys.Up && level==2)
{
dy -= 3;
}else if(e.KeyCode == Keys.Left && level== 2)
{
dx += 2;
}else if(e.KeyCode == Keys.Right && level == 2)
{
dx -= 2;
}
}//end keydown
private void moveShip()
{
//change x and check for boundaries
x += dx;
if (x > this.Width - picLander.Width)
{
x = 0;
}
if (x < 0)
{
x = Convert.ToDouble(this.Width - picLander.Width);
}//end if
//change y and check for boundaries
y += dy;
if (y < 0)
{
y = Convert.ToDouble(this.Height - picLander.Height);
}//end if
if (y > this.Height - picLander.Height)
{
y = 0;
}//end if
//move picLander to new location
picLander.Location = new Point(Convert.ToInt32(x), Convert.ToInt32(y));
}//end MoveShip
private void theForm_Load(object sender, EventArgs e)
{
}
}
You can use the 'Tick' event of the timer.
public Form1()
{
InitializeComponent();
Timer timer = new Timer();
timer.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
//Your code goes here
}
For a school project I need to make a small game where multiple Ellipses move.
The last one that gets made with my method to make multiple at the same time moves with the timer I make.
How do you make one timer for all the Ellipses.
class EnemyTeam
{
private Ellipse enemy;
private Canvas canvas;
private double xChange = 50, yChange = 50;
public DispatcherTimer enemyTimer;
private char direction = '0';
private Thickness enemyThickness;
public EnemyTeam(Canvas canvas, double startPosition, SolidColorBrush playerBrush)
{
this.canvas = canvas;
DrawTeam(canvas, 40, playerBrush);
enemyTimer.Interval = TimeSpan.FromMilliseconds(100);
enemyTimer.Start();
}
private void DrawBall(SolidColorBrush brush, Canvas canvas,double x,double y)
{
enemy = new Ellipse();
enemy.Stroke = brush;
enemy.Fill = brush;
enemy.Height = 30;
enemy.Width = 30;
enemy.Margin = new Thickness(x,y, 0, 0);
enemyTimer = new DispatcherTimer();
enemyThickness = enemy.Margin;
canvas.Children.Add(enemy);
enemyTimer.Tick += enemyTimer_Tick;
}
void enemyTimer_Tick(object sender, EventArgs e)
{
if (enemyThickness.Left >= canvas.Width - 100)
{
GoDown();
direction = '1';
}
if (enemyThickness.Left <= 0 + 20)
{
GoDown();
direction = '0';
}
MoveTeam(enemy);
}
private void MoveTeam(Ellipse enemy)
{
enemyThickness = enemy.Margin;
if (direction == '1')
{
enemyThickness.Left -= xChange;
}
if (direction == '0')
{
enemyThickness.Left += xChange;
}
enemy.Margin = enemyThickness;
}
private void GoDown()
{
enemyThickness.Top += yChange;
enemy.Margin = enemyThickness;
}
}
Instead of initializing and assigning event handler in DrawBall method, do that in constructor of EnemyTeam class. This is will give you on timer per EnemyTeam object.
Declare enemyTimer as a static field:
class EnemyTeam
{
private static enemyTimer = new DispatcherTimer();
...
The keyword static will make the field shared for the class.
You're making multiple timers and throwing them away. See this line:
enemyTimer = new DispatcherTimer();
Every time you call that, you're making a new timer and throwing away the previous copy that enemyTimer held a reference to. Because enemyTimer.Start() is called after DrawTeam, it's called only on the last-created timer. None of the other timers get started.
But even if the other timers got started, you'd still only see one Ellipse move, because in enemyTimer_Tick you only ever make changes to enemy, which is a class member variable that points to the last Ellipse created.
I would suggest that you only use one timer, that you save all the Ellipses you create in a list for later use, and that in enemyTimer_Tick you update all of those Ellipses by iterating through the list.
EDIT: Here is a copy of your code, reworked a bit to show you what I mean. I don't really understand what you're trying to do with MoveTeam and the enemyThickness variable, so I didn't mess with that stuff. That is to say, this isn't a complete working solution, just an example of the changes I'm suggesting.
using System.Collections.Generic;
class EnemyTeam
{
private List<Ellipse> enemies = new List<Ellipse>();
private Canvas canvas;
private double xChange = 50, yChange = 50;
public DispatcherTimer enemyTimer;
private char direction = '0';
private Thickness enemyThickness;
public EnemyTeam(Canvas canvas, double startPosition, SolidColorBrush playerBrush)
{
this.canvas = canvas;
DrawTeam(canvas, 40, playerBrush);
enemyTimer = new DispatcherTimer();
enemyTimer.Interval = TimeSpan.FromMilliseconds(100);
enemyTimer.Tick += enemyTimer_Tick;
enemyTimer.Start();
}
private void DrawBall(SolidColorBrush brush, Canvas canvas,double x,double y)
{
enemy = new Ellipse();
enemy.Stroke = brush;
enemy.Fill = brush;
enemy.Height = 30;
enemy.Width = 30;
enemy.Margin = new Thickness(x,y, 0, 0);
enemyThickness = enemy.Margin; // what is this supposed to do?
canvas.Children.Add(enemy);
enemies.Add(enemy);
}
void enemyTimer_Tick(object sender, EventArgs e)
{
foreach (Ellipse enemy in enemies)
{
if (enemyThickness.Left >= canvas.Width - 100)
{
GoDown();
direction = '1';
}
if (enemyThickness.Left <= 0 + 20)
{
GoDown();
direction = '0';
}
MoveTeam(enemy);
}
}
private void MoveTeam(Ellipse enemy)
{
enemyThickness = enemy.Margin;
if (direction == '1')
{
enemyThickness.Left -= xChange;
}
if (direction == '0')
{
enemyThickness.Left += xChange;
}
enemy.Margin = enemyThickness;
}
private void GoDown()
{
enemyThickness.Top += yChange;
enemy.Margin = enemyThickness;
}
}
I've 4 panels, having same Y and different X, that are created at the program start on a picturebox. When I click on a panel, it sets the focus and is ready to get a keysDown event so i.e. if I click on up arrow key the panel moves up.
This is the code:
public partial class FormView : Form
{
List<CircleButton> myPanels = new List<CircleButton>(); // array of panels CircleButton
Point[] arrayPoints_milestones; // array of X,Y
int index;
public FormView()
{
InitializeComponent();
arrayPoints_milestones = new Point[4];
for (int i = 0; i < 4; i++ )
{
arrayPoints_milestones[i] = new Point { X = 20, Y = 20 };
}
test();
}
protected void panel_Click(object sender, EventArgs e)
{
myPanels[index].PreviewKeyDown -= new PreviewKeyDownEventHandler(panel_KeyDown);
CircleButton panel = sender as CircleButton;
index = (int)panel.Tag;
myPanels[index].Focus(); //panel.Focus();
myPanels[index].PreviewKeyDown += new PreviewKeyDownEventHandler(panel_KeyDown);
}
private void panel_KeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Up)
{
myPanels[index].Centre = new Point(myPanels[index].Centre.X, myPanels[index].Centre.Y - 10);
MessageBox.Show("" + myPanels[index].Centre.Y);
Invalidate();
}
if (e.KeyCode == Keys.Down)
{
myPanels[index].Centre = new Point(myPanels[index].Centre.X, myPanels[index].Centre.Y + 10);
MessageBox.Show("" + myPanels[index].Centre.Y);
Invalidate();
}
}
private void test()
{
//for (int i = 0; i < 4; i++)
int i=0;
foreach(var value in arrayPoints_milestones)
{
CircleButton panel = new CircleButton();
panel.Tag = i;
panel.Centre = new Point(arrayPoints_milestones[i].X + i * 10, arrayPoints_milestones[i].Y);
panel.Radius = 10;
panel.BackColor = Color.Red;
panel.Message = "Index: " + panel.Tag.ToString();
myPanels.Add(panel); // qui aggiungo il pannello alla lista di pannelli myPanels
pictureBox1.Controls.Add(myPanels[i]);
myPanels[i].Click += new EventHandler(panel_Click);
i++;
}
}
}
and this is the custom panel class:
public class CircleButton : Panel
{
//Properties to draw circle
float radius;
public float Radius
{
get { return radius; }
set
{
radius = value;
this.Size = new Size((int)Radius, (int)Radius);
}
}
public string Name
{
get;
set;
}
Point centre;
public Point Centre
{
get { return centre; }
set
{
centre = value;
this.Location = Centre;
}
}
public string Message { get; set; }
public CircleButton()
{
//Default Values
Name = "panel_base";
this.BackColor = Color.Black;
Radius = 1;
Centre = new Point(0, 0);
this.DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//Defines a graphic path and set it as the panel's region
//For custom region use different path's
if (centre != null)
{
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, radius, radius);
this.Region = new Region(path);
path.Dispose();
}
}
}
Each time you click a panel, a PreviewKeyDownEventHandler is added - so 3 clicks will trigger 3 (different) eventhandlers with the same invocation target, and each will move your panel for 10 pixels up/down:
protected void panel_Click(object sender, EventArgs e) {
CircleButton panel = sender as CircleButton;
index = (int)panel.Tag;
myPanels[index].Focus(); //panel.Focus();
myPanels[index].PreviewKeyDown += new PreviewKeyDownEventHandler(panel_KeyDown);
}
Updated code for FormView:
public partial class FormView : Form {
List<CircleButton> myPanels = new List<CircleButton>(); // local use only in my example
Point[] arrayPoints_milestones; //not needed anymore
int index; //not needed anymore
public FormView() {
InitializeComponent();
this.Load += FormView_Load;
}
void FormView_Load(object sender, EventArgs args) {
Point panelOffset = new Point(20, 20);
for (int i = 0; i < 4; i++) {
var panel = new CircleButton() {
Name = "panel" + i, //Attention! You have hidden the property "Name" in "Control" with a re-declaration in "CircleButton"
Tag = i, //not needed anymore, right?
Centre = new Point(panelOffset.X + i * 10, panelOffset.Y),
Radius = 10,
BackColor = Color.Red,
Message = "Index: " + i.ToString(),
};
panel.Click += (s, e) => {
panel.Focus();
};
panel.PreviewKeyDown += (s, e) => {
if(e.KeyCode == Keys.Up) {
Point centre = panel.Centre; //copy value
centre.Y -= 10;
panel.Centre = centre; //assign modified copy
Invalidate();
}
if(e.KeyCode == Keys.Down) {
Point centre = panel.Centre; //copy value
centre.Y += 10;
panel.Centre = centre; //assign modified copy
Invalidate();
}
};
myPanels.Add(panel);
pictureBox1.Controls.Add(panel);
}
}
}
Basically, i have an endless number of blocks (each built from class "Enemy") being create, stored into a list, and sent animated across the screen. It does this forever. I want to delete the first block after 100 blocks have been created so as not to use too much processing power. Any ideas?
THIS IS THE CODE FOR THE WHOLE CLASS FOR WHICH THE OBJECTS I WANT TO DELETE:
namespace MovementTestV1
{
class Enemy
{
protected Dispatcher dispatcher;
protected Canvas Background;
protected Label Display;
Int32 waitTime;
double EnemyWidth = 53;
Image EnemyImage;
String FilePathImage;
BitmapImage bitPic;
protected double x, y;
System.Windows.Forms.Timer tmr;
double incrementSize = 5.0;
private int i = 0;
public Enemy(Canvas Background, Dispatcher dispatcher, Dictionary<String, String> keys,Label Display, Int32 waitTime = 100)
{
this.Background = Background;
this.dispatcher = dispatcher;
this.waitTime = 70;
//this.keys = keys;
this.Display = Display;
EnemyImage = new Image();
EnemyImage.Width = EnemyWidth;
FilePathImage = #"RedSqare.png";
bitPic = LoadBitmap(FilePathImage, EnemyWidth);
//tmr = new System.Windows.Forms.Timer();
//tmr.Interval = this.waitTime;
//tmr.Tick += new EventHandler(Position);
//tmr.Start();
}
protected BitmapImage LoadBitmap(String assetsRelativePath, double decodeWidth)
{
BitmapImage theBitmap = new BitmapImage();
theBitmap.BeginInit();
String basePath = System.IO.Path.Combine(Environment.CurrentDirectory, #"assets\");
String path = System.IO.Path.Combine(basePath, assetsRelativePath);
theBitmap.UriSource = new Uri(path, UriKind.Absolute);
theBitmap.DecodePixelWidth = (int)decodeWidth;
theBitmap.EndInit();
return theBitmap;
}
public void Place(double x, double y)
{
EnemyImage.Source = bitPic;
this.x = x;
this.y = y;
Background.Children.Add(EnemyImage);
EnemyImage.SetValue(Canvas.LeftProperty, x);
EnemyImage.SetValue(Canvas.TopProperty, y);
tmr = new System.Windows.Forms.Timer();
tmr.Interval = 10;
tmr.Tick += new EventHandler(Position);
tmr.Start();
}
public void Position(object sender, System.EventArgs e)
{
i++;
if (i < 9000)
{
x -= incrementSize *.3;
}
UpdatePosition();
}
void UpdatePosition()
{
EnemyImage.SetValue(Canvas.LeftProperty, x);
EnemyImage.SetValue(Canvas.TopProperty, y);
}
public double X
{
get
{
return x;
}
set
{
x = value;
}
}
public double Y
{
get
{
return y;
}
set
{
y = value;
}
}
public void Shutdown()
{
tmr.Stop();
}
}
}
public void spawn(object sender, System.EventArgs e)
{
Int32 place = random.Next(1, 4);
Enemy enemy;
i += 2;
if (i % 46 == 0)
{
Int32 Ycoord = random.Next(0, 700);
switch (place)
{
case 1:
enemy = new Enemy(Background, dispatcher, keys, Display, 10);
enemy.Place(1080, Ycoord);
break;
case 2:
enemy = new Enemy(Background, dispatcher, keys, Display, 10);
enemy.Place(1080, Ycoord);
break;
default:
enemy = new Enemy(Background, dispatcher, keys, Display, 10);
enemy.Place(1080, Ycoord);
break;
}
enemies.Add(enemy);
}
if (enemies.Count > 5)
{
//THIS PART DOESNT WORK!!!!!
enemies.RemoveAt(0);
//enemies[1] = 0;
////enemies[2] = null;
//enemies[2].Shutdown();
////enemies[3] = null;
//enemies[3].Shutdown();
////enemies[4] = null;
//enemies[4].Shutdown();
}
}
Hard to say without any code to work off of... However, you could just check to see if your list has over 100 blocks, then setting your first blocks to null. C# has a garbage collector that will clean up the mess.
Edit: or use the Remove method stated above.
for ( int i = 0; i < enemies.size(); ++i )
{
if ( enemies.Count > 5 )
{
enemies.Remove(i);
}
}
I created a modified Pacman, but I want to add a firebolt shooting out from the mouth of the Pacman. My code is:
namespace TestingPacman
{
class Firebolt
{
Bitmap firebolt0 = null;
Bitmap firebolt1 = null;
public Point fireboltposition;
int fireboltwidth = 0;
int fireboltheight = 0;
public Firebolt(int x, int y)
{
fireboltposition.X = x;
fireboltposition.Y = y;
if (firebolt0 == null)
firebolt0 = new Bitmap("firebolt0.gif");
if (firebolt1 == null)
firebolt1 = new Bitmap("firebolt1.gif");
int fireboltwidth = firebolt0.Width;
int fireboltheight = firebolt0.Height;
}
public Rectangle GetFrame()
{
Rectangle Labelrec = new Rectangle(fireboltposition.X, fireboltposition.Y, fireboltwidth, fireboltheight);
return Labelrec;
}
public void Draw(Graphics g)
{
Rectangle fireboltdecR = new Rectangle(fireboltposition.X, fireboltposition.Y, fireboltwidth, fireboltheight);
Rectangle fireboltsecR = new Rectangle(0, 0, fireboltwidth, fireboltheight);
g.DrawImage(firebolt0, fireboltdecR, fireboltsecR, GraphicsUnit.Pixel);
}
}
How can I make a firebolt move in the direction the pacman is facing?
I have a form1 that when I press "F" it will fire a firebolt
but it cant seem to produce the firebolt image. Why is that?
namespace TestingPacman
{
public partial class Form1 : Form
{
// int inc = 0;
Eater TheEater = new Eater(100,100);
TimeDisplay time = new TimeDisplay();
int sec = 0;
Score score = new Score();
int countofeaten=0;
Random r = new Random();
private List<Label> redlabels = new List<Label>();
private List<Label> bluelabels = new List<Label>();
Firebolt firebolt;
List<Firebolt> listfirebolt = new List<Firebolt>();
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(Brushes.White, 0, 0, this.ClientRectangle.Width, ClientRectangle.Height);
TheEater.Draw(g);
foreach(Firebolt f in listfirebolt)
f.Draw(g);
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
timer1.Enabled = true;
string result = e.KeyData.ToString();
Invalidate(TheEater.GetFrame());
switch (result)
{
case "D1":
if (TheEater.eaterwidth >= 9 && TheEater.eaterheight >= 9)
{
TheEater.eaterwidth++;
TheEater.eaterheight++;
}
break;
case "F":
listfirebolt.Add(firebolt = new Firebolt(TheEater.Position.X, TheEater.Position.Y));
Invalidate(firebolt.GetFrame());
break;
case "D2":
if (TheEater.eaterwidth > 10 && TheEater.eaterheight > 10)
{
TheEater.eaterwidth--;
TheEater.eaterheight--;
}
break;
case "D9": TheEater.inc=TheEater.inc+2;
break;
case "D0": TheEater.inc=TheEater.inc-2;
break;
case "Left":
TheEater.MoveLeft(ClientRectangle);
Invalidate(TheEater.GetFrame());
break;
case "Right":
TheEater.MoveRight(ClientRectangle);
Invalidate(TheEater.GetFrame());
break;
case "Up":
TheEater.MoveUp(ClientRectangle);
Invalidate(TheEater.GetFrame());
break;
case "Down":
TheEater.MoveDown(ClientRectangle);
Invalidate(TheEater.GetFrame());
break;
default:
break;
}
RemoveifIntersected();
}
label2.Text = score.Iskore.ToString();
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = time.FormatTime(sec++);
}
}
}
Jeo, what you are missing in your code is the concept of "Time" as far as I can tell your game only reacts when you press keys. What you really need is a mechanism to pass time in your game. This is almost always done in games with repetitive calls to something called "A Game Loop". Here's a quick example of a game loop that might work for you
class Mob
{
float XPos;
float YPos;
float XVel;
float YVel;
}
List<Mob> EveryThingMovable = new List<Mob>();
void GameLoop() //This loop is called 30 times every second... use a timer or whatever, there are far more sophisticated models, but for a first attaempt at a game it's easiest.
{
MoveEverybody(); //make a function that moves everything that can move
//Later you will have to add collision detection to stop pacman from moving through walls
CollideFireballs(); //Check if a fireball hits the bad guys
//More game stuff...
}
void MoveEverybody()
{
foreach(Mob dude in EverythingMovable)
{
ifDoesntHitWall(dude)
{
dude.XPos += dude.XVel;
dude.YPos += dude.YVel;
}
}
}
anyways, read up on the idea of a Game Loop, I think it's the biggest hurtle you haven't passed in order to move ahead.