德尔福自定义动画 - 碰撞检测 [英] Delphi custom animation - collision detection
问题描述
我与自定义绘图/ 2D动画工作,我想弄清楚如何当移动物体在地图上墙碰撞检测。用户持有箭头键在键盘上移动的对象,并在地图上被存储为点的数组结构。在地图上的壁可以是成角度的,但没有弯曲的壁。
I'm working with custom drawing / 2D animation and I'm trying to figure out how to detect when the moving object collides with a wall in the map. User holds arrow keys on the keyboard to move the object, and the map is stored as an array structure of points. The walls in the map may be angled, but no curved walls.
使用地图结构( FMAP:TMap的;
)在我的code以下,在 DoMove
财产,我怎么发现如果对象是一个在地图和prevent任何城墙,从通过移动碰撞?在 DoMove
,我需要阅读 FMAP
(参见 DrawMap
怎么看 FMAP
作品),并以某种方式确定对象将至任何墙壁并停止它。
Using the map structure (FMap: TMap;
) in my code below, in the DoMove
property, how do I detect if the object is colliding with any wall in the map and prevent it from moving through? In DoMove
, I need to read FMap
(refer to DrawMap
to see how FMap
works) and somehow determine if the object is approaching any wall and stop it.
我可以做双重的X / Y循环迭代中的每个地图的每个部分的每两个点之间的每一种可能的像素,但我已经知道这将是沉重的,考虑到这一程序,只要将被迅速地称为目标移动
I could do a dual X/Y loop iterating every possible pixel between each two points in each part of each map, but I already know this will be heavy, considering this procedure will be called rapidly so long as the object is moving.
我想到的方向读取像素颜色物体的运动,如果有任何黑色(从图线),认为这是一堵墙。但最终会有一个后台的多个自定义绘制,所以读书的像素颜色是行不通的。
I thought of reading the pixel colors in the direction the object's moving, and if there's any black (from map lines), consider it a wall. But eventually there will be more custom drawing of a background, so reading pixel colors wouldn't work.
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 - 这code只是一个剥离和空置我整个项目的版本来演示如何工作
PS - This code is just a stripped and dummied version of my full project to demonstrate how things work.
修改
我才意识到一个重要的因素:现在,我只实现一个移动对象。但是,将有多个移动物体为好。因此,可以用任何一张地图墙或其他物体(我将在一个列表中的每个对象)发生碰撞。整个项目仍然是这样的样本,但更code非常原始的比对有关这个问题的。
I just realized an important factor: Right now, I've only implemented one moving object. However, there will be multiple moving objects as well. So, the collision may occur with either a map wall or another object (which I'll have each object in a list). The full project is still very raw like this sample, but much more code than is relevant for this question.
推荐答案
我已经中途回答我自己的问题在问题自身。有一件事我原以为在移动的方向被读取图像的像素,并检查是否有一条线有或没有。我现在意识到,我可以有背景的 FBMap
图层下一个额外层,并保留图层,因为它是只画的墙壁发生碰撞。
I had already half-way answered my own question in the question its self. One thing I had thought of was reading the pixels of the image in the direction of the movement, and check if there's a line there or not. I now realize that I can have an extra layer under the FBMap
map layer for the background, and leave the map layer as it is with only the collidable walls drawn.
移动时,扫描在该特定层,不是完整的图像上的运动方向的像素。因为我已经有一个pre-绘制层坐在那里,我可以读它,而不是主图像。基于移动的速度,我只需要看看这么多的像素提前(至少是几个像素比运动的像素数)。
When moving, scan the pixels in the direction of the movement on that particular layer, not the full image. Since I already have a pre-drawn layer sitting there, I can read it rather than the main image. Based on the speed of movement, I only need to look so many pixels ahead (at least a few more pixels than the number of pixels of movement).
此外,如果在图像的背景有重新presenting的壁,而不是直的滑动行的图像,然后该层甚至没有在所有要被绘制。这一层可以明确地只用于碰撞领域领先扫描运动的几个像素。作为事实上,因为我还需要认识到的碰撞与其它移动物体,我可以画以及所有在这里的对象(黑色/白色)。
Also, in case the background of the image has a picture representing the walls rather than straight plain lines, then this layer doesn't even have to be drawn at all. This layer can be explicitly used just for scanning a few pixels ahead of movement for collision areas. As a matter of fact, since I also need to recognize collision with other moving objects, I can draw all the objects on here as well (in black/white).
在整个画布像素的几次迭代,例如20,是没有什么比通过地图线广泛迭代,例如2000
A few iterations of pixels across a canvas, for example 20, is nothing compared to extensive iterations through the map lines, for example 2000.
这篇关于德尔福自定义动画 - 碰撞检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!