Э-э, я полагаю, что это домашнее задание, поскольку вы делаете игру в winforms, поэтому я не буду писать вам код, но расскажу, как идет рабочий процесс.
Поскольку у вас есть часть движения, я не буду это объяснять.
То, что вы ищете, это Colliding
.Что это означает, когда два объекта fictional borders
сталкиваются, что делать (например, когда они касаются себя).
Fictional borders
называются Colliders
, и вам нужно создать collider
дляваш player
и для какого-то объекта.Collider
может быть одним объектом (квадратом) или несколькими объектами (несколько квадратов, кружков ....).Для этого примера мы создадим colliders
с простыми квадратами, так как ваша игра 2d.
![](https://i.stack.imgur.com/S0izt.png)
Как вы видите, мы создали коллайдер для игрока (зеленый) и коллайдер для объекта (желтый).
Как видите, игроки сталкиваются с коллайдером объектов.В тот момент, когда эти два сталкиваются, мы поднимаем событие и что-то делаем.
У этого есть много решений, некоторые лучше, чем у других, но есть два простых решения, которые вы можете сделать, это следующее:
- Применение силы противоположного направления
- Перемещение в предыдущее местоположение
Применение силы противоположного направления - сложная вещь, и вам необходимо проверить, где сталкивался коллайдер, а затем переместить объект в этом направлении (так как вы не всегда будете стоять на объекте, но вы можете, например, удариться о стену), и для этого требуется некоторый код, который не подходит для начинающего, который делает это только для практики winforms
.
простое решение для вас и будет сделано так:
- Каждые 5 мс, которые вы помните текущую позицию игроков
- Каждые 1 мс вы проверяете, сталкивается ли это с чем-то
- Если этопри столкновении вы перемещаете его обратно на последнюю запомненную позицию.
Теперь вы спрашиваете, почему нужно помнить каждые 5 мс, но не 1 мs.Это потому, что если что-то случится и коллайдер не обнаружит столкновение, но столкнулось, игрок запомнит эту позицию, а когда произойдет столкновение, он вернется к последней сохраненной позиции, но она уже находится внутри стены.Таким образом, мы уменьшаем шансы на это.Конечно, вы тестируете и видите, что подходит вам больше всего.Также этот пример создаст замедленное движение, но, тем не менее, вы не сможете сделать настоящую игру там.
Это только простое решение для выполнения этой работы, но оно не рекомендуется.
Если вы хотитеБолее сложные и точные решения. В Интернете много статей, и я бы посоветовал вам посмотреть, как работает 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)()) вернуть;поэтому, когда он сталкивается, он не будет перемещать объект.Также вы можете реализовать типы Collider
, чтобы сообщить, что некоторые объекты сталкиваются с другими (например, бонусы)