Delphi开发Android用虚拟摇杆VirtualJoystic

6 篇文章 0 订阅
2 篇文章 0 订阅

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《下载地址

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值