Tôi đang làm việc với bản vẽ tùy chỉnh/hoạt ảnh 2D và tôi đang cố gắng tìm ra cách phát hiện khi đối tượng chuyển động va chạm với một bức tường trên bản đồ. Người dùng giữ các phím mũi tên trên bàn phím để di chuyển đối tượng và bản đồ được lưu trữ dưới dạng cấu trúc mảng của các điểm. Các bức tường trong bản đồ có thể được góc cạnh, nhưng không có các bức tường cong.Hoạt ảnh tùy chỉnh Delphi - phát hiện va chạm
Sử dụng cấu trúc bản đồ (FMap: TMap;
) trong mã của tôi bên dưới, trong thuộc tính DoMove
, làm cách nào để phát hiện đối tượng đang va chạm với bất kỳ bức tường nào trên bản đồ và ngăn không cho nó di chuyển qua? Trong DoMove
, tôi cần phải đọc FMap
(tham khảo DrawMap
để xem cách hoạt động của FMap
) và bằng cách nào đó xác định xem đối tượng có đang tiếp cận bất kỳ bức tường nào và dừng nó không.
Tôi có thể thực hiện một vòng lặp X/Y lặp lại mọi pixel có thể giữa mỗi điểm trong mỗi phần của mỗi bản đồ, nhưng tôi đã biết điều này sẽ rất nặng, xem xét thủ tục này sẽ được gọi nhanh chóng miễn là đối tượng di chuyển.
Tôi đã nghĩ về việc đọc các màu pixel theo hướng di chuyển của đối tượng và nếu có màu đen (từ các đường bản đồ), hãy xem đó là một bức tường. Nhưng cuối cùng sẽ có nhiều bản vẽ tùy chỉnh hơn của nền, vì vậy việc đọc các màu pixel sẽ không hoạt động.
uMain.pas
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
const
//Window client size
MAP_WIDTH = 500;
MAP_HEIGHT = 500;
type
TKeyStates = Array[0..255] of Bool;
TPoints = Array of TPoint;
TMap = Array of TPoints;
TForm1 = class(TForm)
Tmr: TTimer;
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure TmrTimer(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
FBMain: TBitmap; //Main rendering image
FBMap: TBitmap; //Map image
FBObj: TBitmap; //Object image
FKeys: TKeyStates; //Keyboard states
FPos: TPoint; //Current object position
FMap: TMap; //Map line structure
procedure Render;
procedure DrawObj;
procedure DoMove;
procedure DrawMap;
procedure LoadMap;
public
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
Math, StrUtils;
procedure TForm1.FormCreate(Sender: TObject);
begin
FBMain:= TBitmap.Create;
FBMap:= TBitmap.Create;
FBObj:= TBitmap.Create;
ClientWidth:= MAP_WIDTH;
ClientHeight:= MAP_HEIGHT;
FBMain.Width:= MAP_WIDTH;
FBMain.Height:= MAP_HEIGHT;
FBMap.Width:= MAP_WIDTH;
FBMap.Height:= MAP_HEIGHT;
FBObj.Width:= MAP_WIDTH;
FBObj.Height:= MAP_HEIGHT;
FBObj.TransparentColor:= clWhite;
FBObj.Transparent:= True;
FPos:= Point(150, 150);
LoadMap; //Load map lines into array structure
DrawMap; //Draw map lines to map image only once
Tmr.Enabled:= True;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
Tmr.Enabled:= False;
FBMain.Free;
FBMap.Free;
FBObj.Free;
end;
procedure TForm1.LoadMap;
begin
SetLength(FMap, 1); //Just one object on map
//Triangle
SetLength(FMap[0], 4); //4 points total
FMap[0][0]:= Point(250, 100);
FMap[0][1]:= Point(250, 400);
FMap[0][2]:= Point(100, 400);
FMap[0][3]:= Point(250, 100);
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
FKeys[Key]:= True;
end;
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
FKeys[Key]:= False;
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
Canvas.Draw(0, 0, FBMain); //Just draw rendered image to form
end;
procedure TForm1.DoMove;
const
SPD = 3; //Speed (pixels per movement)
var
X, Y: Integer;
P: TPoints;
begin
//How to keep object from passing through map walls?
if FKeys[VK_LEFT] then begin
//Check if there's a wall on the left
FPos.X:= FPos.X - SPD;
end;
if FKeys[VK_RIGHT] then begin
//Check if there's a wall on the right
FPos.X:= FPos.X + SPD;
end;
if FKeys[VK_UP] then begin
//Check if there's a wall on the top
FPos.Y:= FPos.Y - SPD;
end;
if FKeys[VK_DOWN] then begin
//Check if there's a wall on the bottom
FPos.Y:= FPos.Y + SPD;
end;
end;
procedure TForm1.DrawMap;
var
C: TCanvas;
X, Y: Integer;
P: TPoints;
begin
C:= FBMap.Canvas;
//Clear image first
C.Brush.Style:= bsSolid;
C.Pen.Style:= psClear;
C.Brush.Color:= clWhite;
C.FillRect(C.ClipRect);
//Draw map walls
C.Brush.Style:= bsClear;
C.Pen.Style:= psSolid;
C.Pen.Width:= 2;
C.Pen.Color:= clBlack;
for X := 0 to Length(FMap) - 1 do begin
P:= FMap[X]; //One single map object
for Y := 0 to Length(P) - 1 do begin
if Y = 0 then //First iteration only
C.MoveTo(P[Y].X, P[Y].Y)
else //All remaining iterations
C.LineTo(P[Y].X, P[Y].Y);
end;
end;
end;
procedure TForm1.DrawObj;
var
C: TCanvas;
R: TRect;
begin
C:= FBObj.Canvas;
//Clear image first
C.Brush.Style:= bsSolid;
C.Pen.Style:= psClear;
C.Brush.Color:= clWhite;
C.FillRect(C.ClipRect);
//Draw object in current position
C.Brush.Style:= bsClear;
C.Pen.Style:= psSolid;
C.Pen.Width:= 2;
C.Pen.Color:= clRed;
R.Left:= FPos.X - 10;
R.Right:= FPos.X + 10;
R.Top:= FPos.Y - 10;
R.Bottom:= FPos.Y + 10;
C.Ellipse(R);
end;
procedure TForm1.Render;
begin
//Combine map and object images into main image
FBMain.Canvas.Draw(0, 0, FBMap);
FBMain.Canvas.Draw(0, 0, FBObj);
Invalidate; //Repaint
end;
procedure TForm1.TmrTimer(Sender: TObject);
begin
DoMove; //Control movement of object
DrawObj; //Draw object
Render;
end;
end.
uMain.dfm
object Form1: TForm1
Left = 315
Top = 113
BorderIcons = [biSystemMenu]
BorderStyle = bsSingle
Caption = 'Form1'
ClientHeight = 104
ClientWidth = 207
Color = clBtnFace
DoubleBuffered = True
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
OnDestroy = FormDestroy
OnKeyDown = FormKeyDown
OnKeyUp = FormKeyUp
OnPaint = FormPaint
PixelsPerInch = 96
TextHeight = 13
object Tmr: TTimer
Enabled = False
Interval = 50
OnTimer = TmrTimer
Left = 24
Top = 8
end
end
PS - Đoạn mã này chỉ là một tước và dummied phiên bản của dự án đầy đủ của tôi để chứng minh cách mọi thứ hoạt động.
EDIT
Tôi chỉ nhận ra một yếu tố quan trọng: Ngay bây giờ, tôi đã chỉ thực hiện một đối tượng chuyển động. Tuy nhiên, sẽ có nhiều đối tượng chuyển động. Vì vậy, sự va chạm có thể xảy ra với một bức tường bản đồ hoặc đối tượng khác (mà tôi sẽ có mỗi đối tượng trong một danh sách). Dự án đầy đủ vẫn còn rất thô như mẫu này, nhưng nhiều mã hơn là có liên quan cho câu hỏi này.
Collision phát hiện không có gì để làm với các phần trình bày bởi vì bạn nên luôn luôn riêng biệt logic từ bản trình bày. –
Và nếu tôi hỏi câu hỏi này mà không có mã của tôi, mọi người sẽ phàn nàn rằng họ không thể trả lời câu hỏi không có mã. –
Hãy xem http://www.partow.net/projects/fastgeo/index.html (cũ của nó nhưng phải là toán học thuần túy) –