[英]How could I make a picturebox to become like a platform that my player can jump into?
我已經有一個玩家移動和跳躍使用間隔1ms的定時器控制,我的問題是我無法制作一個圖片框成為一個平台,這是我的角色移動代碼
public partial class Form1 : Form
{
bool left, right;
bool jump;
int G = 20;
int Force;
public Form1()
{
InitializeComponent();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Right) { right = true; }
if (e.KeyCode == Keys.Left) { left = true; }
if (jump != true)
{
if (e.KeyCode == Keys.Space)
{
jump = true;
Force = G;
}
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Right) { right = false; }
if (e.KeyCode == Keys.Left) { left = false; }
}
private void timer1_Tick(object sender, EventArgs e)
{
if (right == true) { player.Left += 5; }
if (left == true) { player.Left -= 5; }
if (jump == true)
{
//Falling (if the player has jumped before)
player.Top -= Force;
Force -= 1;
}
if (player.Top + player.Height >= screen.Height)
{
player.Top = screen.Height - player.Height; //Stop falling at bottom
jump = false;
}
else
{
player.Top += 5;//Falling
}
}
}
我正在使用 winforms 來創建這個游戲,因為這是必需的。
呃,我想這是一些功課,因為您正在使用 winforms 制作游戲,所以我不會為您編寫代碼,而是告訴您工作流程是如何進行的。
既然你有運動部分,我就不解釋了。
您正在尋找的是Colliding
。 這意味着當兩個物體fictional borders
發生碰撞時,該怎么辦(當它們接觸到自己時)。
Fictional borders
稱為Colliders
,您需要做的是為您的player
和某些對象創建collider
player
。 Collider
可以是單個對象(正方形)或多個對象(多個正方形、圓形......)。 在本例中,我們將創建帶有簡單方塊的colliders
,因為您的游戲是 2d。
如您所見,我們為玩家(綠色)創建了碰撞器,為對象(黃色)創建了碰撞器。
如您所見,玩家對撞機正在與對象對撞機發生碰撞。 在這兩者發生碰撞的那一刻,我們引發事件並做一些事情。
有些東西有很多解決方案,有些比其他解決方案更好,但接下來你可以做兩個簡單的解決方案:
施加相反方向的力是一件復雜的事情,需要您檢查碰撞器在哪里發生碰撞,然后將對象移動到該方向(因為您不會總是站在對象上,但例如您可能會撞到牆壁)並且它需要一些代碼不適合只是為了練習winforms
初學者。
移動到以前的位置對您來說是一個簡單的解決方案,可以這樣完成:
現在你會問為什么要記住每 5 毫秒而不是 1 毫秒。 這是因為如果發生了某些事情並且對撞機沒有檢測到碰撞但確實發生了碰撞,玩家會記住那個位置,當發生碰撞時,它會回到上次保存的位置,但那個位置已經在牆內了。 這樣我們就減少了這種情況發生的機會。 當然,您可以進行測試,看看什么最適合您。 此外,此示例將創建滯后運動,但您仍然不會在其中制作真正的游戲。
這是完成這項工作的唯一簡單解決方案,但不推薦使用。
如果您想要更復雜和精確的解決方案,互聯網上有很多文章,我建議您查看Unity 2d collision
工作原理並將其轉換為您的代碼。
因為我對在 winforms 中做這件事很感興趣,所以我也做了它。
這是我使用的代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
namespace Game
{
public partial class Form1 : Form
{
Player player;
Obsticle obsticle;
Thread UpdateThread;
public const int Gravity = 1;
public Form1()
{
InitializeComponent();
InitializeObjects();
UpdateThread = new Thread(() => {
System.Timers.Timer UpdateTimer = new System.Timers.Timer();
UpdateTimer.Elapsed += new System.Timers.ElapsedEventHandler(Update);
UpdateTimer.Interval = 2;
UpdateTimer.Enabled = true;
});
UpdateThread.Start();
}
private void InitializeObjects()
{
PictureBox pb = new PictureBox();
pb.BackgroundImage = global::Game.Properties.Resources.SuperMario;
pb.BackgroundImageLayout = ImageLayout.Stretch;
pb.Location = new Point(47, 59);
pb.Name = "Player";
pb.Size = new Size(76, 72);
pb.TabIndex = 0;
pb.TabStop = false;
player = new Player(this, pb);
PictureBox pb1 = new PictureBox();
pb1.BackgroundImage = global::Game.Properties.Resources.Box;
pb1.BackgroundImageLayout = ImageLayout.Stretch;
pb1.Location = new Point(47, 226);
pb1.Name = "Obsticle";
pb1.Size = new Size(100, 95);
pb1.TabIndex = 0;
pb1.TabStop = false;
obsticle = new Obsticle(this, pb1);
}
private void Update(object sender, ElapsedEventArgs e)
{
player.ApplyGravity(Gravity);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
player.Collider.DrawCollider(e.Graphics);
obsticle.Collider.DrawCollider(e.Graphics);
}
}
public class Object
{
public static List<Object> Objects = new List<Object>();
public Form Handler;
public PictureBox Picture { get; set; }
public BoxCollider Collider { get; set; }
public int x
{
get
{
return _x;
}
set
{
_x = value;
Handler.Invoke(new Action(() => {
Picture.Location = new Point((int)_x, Picture.Location.Y);
Handler.Refresh();
}));
}
}
public int y
{
get
{
return _y;
}
set
{
_y = value;
Handler.Invoke(new Action(() => {
Picture.Location = new Point(Picture.Location.X, _y);
Handler.Refresh();
}));
}
}
private int _x;
private int _y;
public Object(Form handler, PictureBox Picture)
{
this.Handler = handler;
this.Picture = Picture;
_x = Picture.Location.X;
_y = Picture.Location.Y;
handler.Controls.Add(Picture);
Collider = new BoxCollider(this);
Objects.Add(this);
}
public void ApplyGravity(int gravityForce)
{
if (Collider.CheckCollide())
return;
y += gravityForce;
}
}
public class Player : Object
{
public int movementSpeed { get { return _movementSpeed; } }
private int _movementSpeed = 10;
public Player(Form handler, PictureBox Picture) : base(handler, Picture)
{
}
public void MoveDown(int value)
{
y += value;
}
}
public class Obsticle : Object
{
public Obsticle(Form handler, PictureBox Picture) : base(handler, Picture)
{
}
}
public class BoxCollider
{
private Pen Pen_Default = new Pen(Color.Red);
private Object Object;
private Rectangle rect;
public BoxCollider(Object Object)
{
this.Object = Object;
}
public bool CheckCollide()
{
foreach(Object o in Object.Objects)
{
if (rect.IntersectsWith(o.Collider.rect) && o.Collider.rect != rect)
return true;
}
return false;
}
public void DrawCollider(Graphics g)
{
rect = new Rectangle(Object.Picture.Location.X, Object.Picture.Location.Y, Object.Picture.Width, Object.Picture.Height);
Pen_Default.Width = 5;
g.DrawRectangle(Pen_Default, rect);
}
}
}
它沒有運動機制,因為我只想測試碰撞。 如果您復制/粘貼它,您將收到資源錯誤,因此只需添加 2 張圖像並進行測試:)
這段代碼中的一點是,您在更改任何位置變量之前需要調用 if (Collider.CheckCollide()) return; 所以當它碰撞時它不會移動物體。 您也可以實現Collider
類型,以便您告訴某些對象與其他對象發生碰撞(例如電源)
你可以自己檢查代碼。 我在表單中添加了 3 個圖片pictureboxes
。 只是為了簡化問題,表格並未最大化。
代碼:
bool left, right;
bool jump;
int G = 20;
int Force;
List<Rectangle> obstacles = new List<Rectangle>();
添加obstacles
:
public Form1() {
InitializeComponent();
obstacles.Add( new Rectangle( pictureBox1.Left, pictureBox1.Top, pictureBox1.Width, pictureBox1.Height ) );
obstacles.Add( new Rectangle( pictureBox2.Left, pictureBox2.Top, pictureBox2.Width, pictureBox2.Height ) );
obstacles.Add( new Rectangle( pictureBox3.Left, pictureBox3.Top, pictureBox3.Width, pictureBox3.Height ) );
obstacles.Add( new Rectangle( -1, 0, 1, this.ClientSize.Height ) ); //left border of form
obstacles.Add( new Rectangle( this.ClientSize.Width, 0, 1, this.ClientSize.Height ) ); //right border of form
obstacles.Add( new Rectangle( 0, this.ClientSize.Height, this.ClientSize.Width, 1 ) ); //down border of form
obstacles.Add( new Rectangle( 0, -1, this.ClientSize.Height, 1 ) ); //up border of form
}
並在tick
事件中:
private void timer1_Tick( object sender, EventArgs e ) {
Rectangle playerClone = player.Bounds; // new Rectangle( player.Left, player.Top, player.Width, player.Height );
List<int> list = new List<int>();
if( left == true ) {
playerClone.X -= 5;
//Check if we have left colision
int pos = GetIntersectPosL( playerClone );
if( pos >= 0 ) { //there is a colision
playerClone.X = pos;
}
}
else if( right == true ) {
playerClone.X += 5;
//Check if we have right colision
int pos = GetIntersectPosR( playerClone );
if( pos >= 0 ) { //there is a colision
playerClone.X = pos - playerClone.Width;
}
}
if( jump == true ) {
playerClone.Y -= (Force - 5); //the 5 is from falling.
if( Force - 5 >= 0 ) { //going up
//Check if we have up colision, if the top of the player is colliding with anything
int pos = GetIntersectPosU( playerClone );
if( pos >= 0 ) { //there is a colision
playerClone.Y = pos;
}
}
else { //Falling down
//Check if we have down colision, if the bottom of the player is colliding with anything
int pos = GetIntersectPosD( playerClone );
if( pos >= 0 ) { //there is a colision
playerClone.Y = pos - playerClone.Height;
jump = false;
}
}
Force -= 1;
}
else { //Falling without previous jumping
playerClone.Y += 5;
//Check if we have down colision, if the bottom of the player is colliding with anything
int pos = GetIntersectPosD( playerClone );
if( pos >= 0 ) { //there is a colision
playerClone.Y = pos - playerClone.Height;
}
}
player.Bounds = playerClone;
}
private int GetIntersectPosL(Rectangle src) {
List<int> list = new List<int>();
//Get all intersect rectangles and add the right side to the list
for( int i = 0; i < obstacles.Count; i++ ) {
if( src.IntersectsWith( obstacles[ i ] ) == true ) {
list.Add( obstacles[ i ].Right );
}
}
if( list.Count == 0 ) { //no intersect
return -1;
}
return list.Max();
}
private int GetIntersectPosR( Rectangle src ) {
List<int> list = new List<int>();
//Get all intersect rectangles and add the left side to the list
for( int i = 0; i < obstacles.Count; i++ ) {
if( src.IntersectsWith( obstacles[ i ] ) == true ) {
list.Add( obstacles[ i ].Left );
}
}
if( list.Count == 0 ) { //No intersect
return -1;
}
return list.Min();
}
private int GetIntersectPosD( Rectangle src ) {
List<int> list = new List<int>();
//Get all intersect rectangles and add the top side to the list
for( int i = 0; i < obstacles.Count; i++ ) {
if( src.IntersectsWith( obstacles[ i ] ) == true ) {
list.Add( obstacles[ i ].Top );
}
}
if( list.Count == 0 ) { //No intersect
return -1;
}
return list.Min();
}
private int GetIntersectPosU( Rectangle src ) {
List<int> list = new List<int>();
//Get all intersect rectangles and add the bottom side to the list
for( int i = 0; i < obstacles.Count; i++ ) {
if( src.IntersectsWith( obstacles[ i ] ) == true ) {
list.Add( obstacles[ i ].Bottom );
}
}
if( list.Count == 0 ) { //No intersect
return -1;
}
return list.Max();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.