-

     (这篇原文是写在SP论坛上的)

  以前用按键精灵写过一个用键盘控制,一个用鼠标控制人物走路的脚本,基本算法都是参照courser和zard110的思路,总感觉这个算法还有改进的余地,最近hua_mars提出另一种算法,理论上应该是比较有效率的,但要控制屏幕坐标系与地图坐标系平行(hua_mars的说法是角度重合)在实际操作上有一定难度。正好最近在写一个航行控制系统,,对走路和航行的算法深入研究了一番,终于有了点心得,拿出来与大家分享。(废话一箩筐
  大家都知道大航海内存中表示角度是Cos和Sin两个值,为什么不是直接用角度一个值来表示呢?这肯定有其道理。再说我们为实现行走,算来算去最后也就是要一个角的COS值和SIN值,当然此角非那角。能不能不计算角,通过简单运算得到要的值呢?答案是肯定的!下面先来看个坐标示意图:


  Wx:是屏幕坐标系X 轴,Mx 是地图坐标系X轴的平行线,P是人物面对的方向(人物在各线的交汇点上,也就是屏幕中心,为方便计算和控制行走始终使人物正面向上),T是目标方向(在这方向上某个点就是目标)。假设好了,现在来看一下已知条件:
  1.游戏内存读到的人物坐标和角度,分别记为:Px,Py,Cos∠MP,Sin ∠MP
  2.人物在屏幕中的坐标,记为Pwx和Pwy 分别是400和307
  3.通过控制使人物正面向上的角度,∠WP=3/2*PI
  4.要去目标坐标,Tx,Ty
  有了了已知条件,我们要计算什么呢?目的必须明确。目的是要人物走向目标,那就是要求算出二个东东,一是人物与目标距离(D),二是目标在屏幕中方位角(∠WT),也就是屏幕X轴与目标夹角。有了这二个数据,不管你是用鼠标控制还是键盘控制都能轻易做到了。好!下面我们来推算下:
  首先人物与目标的距离(D),这个好算,在三角函数中求二点距离公式就可以了,如下:
  D=SQR((Px-Tx)^2+(Py-Ty)^2)。
  接下来求∠WT的角度,正确地说只要求出这个角的Cos和Sin值就可以了。
  根据已知条件:
  1.屏幕坐标系与地图系的夹角:
               ∠WM=∠WP-∠MP=3/2*PI-∠MP                       
  2.屏幕X轴与目标夹角:
                    ∠WT=∠WM+∠MT
                         =3/2*PI-∠MP+∠MT
   这里∠MT是地图X轴与目标夹角,是多少先放一下,后面再述。
  3.有了∠WT现在来求Cos和Sin值:
         (1)Cos∠WT=Cos(3/2*PI-∠MP+∠MT)
    用三角函数分解一下:
            Cos∠WT=-Sin(∠MP-∠MT)
                =-(Sin∠MP *Cos∠MT -Cos∠MP *Sin∠MT)
                = Cos∠MP *Sin∠MT- Sin∠MP *Cos∠MT
         (2) Sin∠WT=Sin(3/2*PI-∠MP+∠MT)
                        同理,可以得到:
            Sin∠WT=-Cos∠MP *Cos∠MT -Sin∠MP *Sin∠MT
  好了,到此大家应该看出些什么了,Cos∠MP和Sin∠MP是从内存读出来已知值,现在只要知道Cos∠MT和Sin∠MT就可以得到我们要的东东了。
  前面说了∠MT是地图X轴与目标的夹角。注意!这个夹角不是在地图原点(0,0)的夹角,是在人物位置(Px,Py)处的夹角,我在这搞糊涂了好几次,走了几次弯路才搞清楚。这里目标坐标(Tx,Ty)和人物坐标(Px,Py)是已知的,在地图X轴的平行线上画条垂线,就形成了一个直角三角形,如下图:

 

  在前面已经求得从人物P点到目标T点的距离D,那么根据三角函数公式可以得到:
                Cos∠MT=(Tx-Px)/D
                Sin∠MT=(Ty-Py)/D
  到此时我要算的东东,全得到了。按这算法我在VB中已实现正(精)确地陆地行走和海上航行。下面是我在VB中控制航行用的子函数: 

'海上航行线路总控制,使用测量坐标,使用到其他模块的公共参数:
'
路点数(0-n): RoutePoint
'
路点坐标:Route()
'
路点计数:Goal
Public Sub Navigate(nPlay As Play)
    
With nPlay
        
Call WinZControl(.hwnd) '如果是开始先调整上下视角
        If Not WinXControl(.hwnd) Then Exit Sub  '调整人物在窗口中的方向,使正面向上
        If Not Arrivals Then                            '如果已到一个拐点,取下一个路点
            TX = Route(Goal).X
            TY 
= Route(Goal).Y
        
End If
        Arrivals 
= True                                 '行走标记开(True)
        PX = .GaugeX
        PY 
= .GaugeY
        PCos 
= .TCos
        PSin 
= .TSin
        
If Abs(TX - PX) > 8192 Then
            PX 
= PX + Sgn(TX - PX) * 16384
        
End If
        
Dim Dist As Single
        
'计算距离,判定是否到位
        Dist = Distance
        
If Dist < 50 Then
            
'20节速度时约等于6点/每秒,小于5秒的航程判定为到达
            Arrivals = False
            Goal 
= Goal + 1
            
Exit Sub
        
End If
        
'计算角度,判断是否要调整航向
        Call mDBClick(.hwnd, Dist)
    
End With
End Sub
'求鼠标点击点并点击(用于海上,双击)
Private Sub mDBClick(hwnd As Long, ByVal D As Single)
    
Dim Deviation As Single
    
'人物在窗口中的坐标
    Const PWX = 400
    
Const PWY = 320
    Deviation 
= 30 / D
    
If Deviation > Sin(PI / 75Then Deviation = Sin(PI / 75)
    
'如果航向小于误差范围,保持原航向,否则调整航向
    If Abs(WTCos(D)) > Deviation Then
        
Dim R As Long, CX As Integer, CY As Integer
        R 
= D
        
If R > 200 Then R = 200
        CX 
= R * WTCos(D) + PWX
        CY 
= R * WTSin(D) + PWY
        
Call vKM.LeftDCk(hwnd, CX, CY)
    
End If
End Sub
'求人物到目标距离
Private Function Distance() As Single
    
Dim X As Single, Y As Single
    X 
= TX - PX
    Y 
= TY - PY
    Distance 
= Sqr(X * X + Y * Y)
End Function

'求目标以人为原点与地图X轴的夹角的COS和SIN值
Private Function TCos(ByVal D As SingleAs Single
    TCos 
= (TX - PX) / D
End Function

Private Function TSin(ByVal D As SingleAs Single
    TSin 
= (TY - PY) / D
End Function

'求目标在窗口中的角度COS和SIN值
Private Function WTCos(ByVal D As SingleAs Single
    WTCos 
= PCos * TSin(D) - PSin * TCos(D)
End Function

Private Function WTSin(ByVal D As SingleAs Single
    WTSin 
= -PCos * TCos(D) - PSin * TSin(D)
End Function
posted on 2009-03-21 08:29  LazyGod  阅读(2172)  评论(11)    收藏  举报