Delphi11新鲜出炉后一直没有去试用,最近几天有空下了个Lite版来玩,发现编译速度快了很多,编译后的尺寸也变小了一些,前些时候在github上看到一个Delphi无引擎版的小游戏,决定调试一下,目前Windows下运行正常,像这个样子:
安卓下暂时没有运行起来,界面是这个样子:
使用的MouseDown和MouseUp事件驱动,也就是说按左、右的时候,没法跳,所以想着改造一下,整个虚拟摇杆来操控,于是有了这一篇博文(顺便吐槽一下Delphi11有时编辑器会完全消失,欢迎页面也一起消失了)。
一、首先顺便弄一下,做个摇杆的样子,和按键的样子:
二、摇杆在屏幕左半边,按键在屏幕右半边,也就是说点击屏幕左半边任意一点就是 摇杆的中心位置,然后滑动触控点,一通计算得到方向,同时兼顾右边的按键触控即可。
单元代码如下(网上其他的方法太复杂难懂,下面主要就3个方法2个事件):
unit Unit7;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects,
FMX.Gestures, FMX.ExtCtrls, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Ani,
FMX.Memo.Types, FMX.ScrollBox, FMX.Memo, System.Math, FMX.Layouts;
type
TForm7 = class(TForm)
Label1: TLabel;
Label2: TLabel;
chk8Dir: TCheckBox;
Memo1: TMemo;
Layout1: TLayout;
Circle1: TCircle;
Circle2: TCircle;
Circle3: TCircle;
Circle4: TCircle;
joyBorder: TCircle;
joyCenter: TCircle;
tmrJoyUp: TTimer;
Label0: TLabel;
procedure chk8DirChange(Sender: TObject);
procedure FormTouch(Sender: TObject; const Touches: TTouches;
const Action: TTouchAction);
procedure FormCreate(Sender: TObject);
procedure tmrJoyUpTimer(Sender: TObject);
private
FStartPt: TPointF;
FBtns: TArray<TCircle>;
function CalcDirection(A: integer): Char;
procedure JoyDown(X, Y: Single);
procedure JoyMove(X, Y: Single);
procedure JoyUp;
{ Private declarations }
public
{ Public declarations }
end;
var
Form7: TForm7;
implementation
{$R *.fmx}
var
A2D8: array [0..7] of Integer = (23, 68, 112, 158, 203, 248, 293, 338);
A2C8: array [0..7] of Char = ('↑', '↗', '→', '↘', '↓', '↙', '←', '↖');
A2D4: array [0..3] of Integer = (45, 135, 225, 315);
A2C4: array [0..3] of Char = ('↑', '→', '↓', '←');
function TForm7.CalcDirection(A: integer): Char;
var
I: integer;
begin
if chk8Dir.IsChecked then
begin
//计算八方向下当前摇杆的方向
Result := A2C8[0];
for I := 1 to 7 do
if (A >= A2D8[I-1]) and (A < A2D8[I]) then
begin
Result := A2C8[I];
exit;
end;
end
else
begin
//计算四方向下当前摇杆的方向
Result := A2C4[0];
for I := 1 to 3 do
if (A >= A2D4[I-1]) and (A < A2D4[I]) then
begin
Result := A2C4[I];
exit;
end;
end;
end;
procedure TForm7.chk8DirChange(Sender: TObject);
begin
if chk8dir.IsChecked then
chk8dir.Text := '八方向'
else
chk8dir.Text := '四方向';
end;
procedure TForm7.FormCreate(Sender: TObject);
begin
//初始化摇杆按键
SetLength(FBtns, 4);
FBtns[0] := Circle4;
FBtns[1] := Circle3;
FBtns[2] := Circle2;
FBtns[3] := Circle1;
{$IFDEF Android}
FullScreen := True;
{$ENDIF}
Label0.Text := Format('屏幕:%d x %d', [Width, Height]);
end;
procedure TForm7.FormTouch(Sender: TObject; const Touches: TTouches;
const Action: TTouchAction);
var
I, J, R: Integer;
JoyOk, KeyOk: Boolean;
pt: TPoint;
begin
//假设屏幕左半边只能用来操作摇杆,按键必须在屏幕右半边
//不能使用MouseDown\MouseMove\MouseUp事件+Touch事件的方式
//因为第一个触控的点会被当成Mouse事件,也就是说先按按键的话,摇杆就操作不了
//所以要使用OnTouch事件进行多点触控
//另外OnTouch事件有个特点,有触控时才触发,若只操作摇杆再松开的话则无事件触发
//也不能用MouseUp来触发,因为只有第一个点松开时才发触发MouseUp,若一直按差按键
//再滑动摇杆,则摇杆松开也没法产生事件使摇杆复位
//解决方案:用joyCenter.Tag作为标志,当屏幕左半边有点触发时Tag=1,设定一个Timer
//在0.5秒内归0,下一个OnTouch事件中仍有这个点,则再次执行Tag=1
JoyOk := False;
KeyOk := False;
memo1.Lines.BeginUpdate;
memo1.Lines.Clear;
for I := 0 to length(Touches) - 1 do
begin
pt := Touches[I].Location.Round;
memo1.Lines.Add(Format('(%.3d, %.3d)', [pt.X, pt.Y]));
if pt.X < Width/2 then
begin
if JoyOk then
Continue;
JoyOk := True;
tmrJoyUp.Enabled := False;
if joyCenter.Tag = 1 then
JoyMove(pt.X, pt.Y)
else
JoyDown(pt.X, pt.Y);
tmrJoyUp.Enabled := True;
end
//一次最多触发一个按键(摇杆由Mouse事件触发了)
else if KeyOk then
Continue;
//遍历按键
for J := 0 to Length(FBtns) - 1 do
begin
//计算按键半么(按键是圆的)
R := System.Math.Min(Round(FBtns[J].Width / 2), Round(FBtns[J].Height / 2));
//检测触控点是否在按钮区域内
if PtInCircle(pt, FBtns[J].Position.Point.Round + Point(R, R), R) then
begin
KeyOk := True;
//TODO 执行按键操作
FBtns[J].StopPropertyAnimation('fill.color');
FBtns[J].Fill.Color := TAlphaColors.Chartreuse;
FBtns[J].AnimateColor('fill.color', TAlphaColors.Aliceblue, 0.5);
end;
end;
end;
memo1.Lines.EndUpdate;
end;
procedure TForm7.JoyDown(X, Y: Single);
begin
if X >= Layout1.Width/2 then
Exit;
if X < joyBorder.Width/2 then
Exit;
if Y > Layout1.Height-joyBorder.Height/2 then
Exit;
joyBorder.Position.X := X - joyBorder.Width / 2;
joyBorder.Position.Y := Y - joyBorder.Height / 2;
joyCenter.Position.X := (joyBorder.Width - joyCenter.Width) / 2;
joyCenter.Position.Y := (joyBorder.Height - joyCenter.Height) / 2;
if joyCenter.Tag = 0 then
begin
FStartPt.X := X;
FStartPt.Y := Y;
end;
joyCenter.Tag := 1;
Label1.Text := Format('原点:(%.0f, %.0f)', [FStartPt.X, FStartPt.Y]);
end;
procedure TForm7.JoyMove(X, Y: Single);
var
pt: TPointF;
I, A: Integer;
r, mr, aa: Single;
c: Char;
begin
pt.X := X;
pt.Y := Y;
aa := pt.Angle(FStartPt);//弧度
//弧度*180/PI => X轴正方向为0度,X轴顺时针为0~180度,逆时针为0~-180角
//所以+360度 => X轴顺时针0~360,(A+90)%360 => 调整为正北为0度
A := Round(360 + aa*180/3.1415926 + 90) mod 360;
r := pt.Distance(FStartPt);
//最大半径要减去摇杆球的半么
mr := joyBorder.Width/2 - joyCenter.Width/2;
if r > mr then
r := mr;
//使用极坐标公式 x = r*cos(a), y = r*sin(a)通过角度、半径计算出虚拟摇杆球的位置
joyCenter.Position.X := joyBorder.Width/2 + r*Cos(aa) - joyCenter.Width/2;
joyCenter.Position.Y := joyBorder.Height/2 + r*Sin(aa) - joyCenter.Height/2;
C := CalcDirection(A);
Label2.Text := Format('远点:(%.0f, %.0f),方位角:%d°,强度:%.0f%%,方向:%s', [pt.X, pt.Y, A, r/mr*100, c]);
end;
procedure TForm7.JoyUp;
begin
//摇杆复位
joyCenter.Tag := 0;
joyCenter.Position.X := (joyBorder.Width - joyCenter.Width) / 2;
joyCenter.Position.Y := (joyBorder.Height - joyCenter.Height) / 2;
end;
procedure TForm7.tmrJoyUpTimer(Sender: TObject);
begin
tmrJoyUp.Enabled := False;
JoyUp;
memo1.Lines.Clear;
end;
end.
最终效果是这个样子(HUAWEI P20):
完整工程含APK《下载地址》