[VB.net] 再談使圖片捲動的「拉霸遊戲機」(二) - 控制捲動結果和加上運動函數

經過初步測試圖片捲動看似可以了,但是要實際拿來用的話還要補上很多東西,先處理這些吧:
1.要能控制轉動的結果,也就是要讓它正確的轉出事先定義的結果圖片。
2.要能從前一次轉動最後停駐的那張圖片開始運轉,不能每回都從第一張開始。
3.每次的轉動時間長度要不同,這樣將來要做水果盤遊戲時才不會九格幾乎同時停止。
4.要加上運動函數讓轉動顯得自然(起動時由慢至快→然後維持在最高速→停止前由快漸慢到停止)。

經過初步測試圖片捲動看似可以了,但是要實際拿來用的話還要補上很多東西,先處理這些吧:

  1. 要能控制轉動的結果,也就是要讓它正確的轉出事先定義的結果圖片。
  2. 要能從前一次轉動最後停駐的那張圖片開始運轉,不能每回都從第一張開始。
  3. 每次的轉動時間長度要不同,這樣將來要做水果盤遊戲時才不會九格幾乎同時停止。
  4. 要加上運動函數讓轉動顯得自然(起動時由慢至快→然後維持在最高速→停止前由快漸慢到停止)。

 

做法分享:

1、如何控制最後停止的位置,令轉動停止在事先約定的圖片上面。

  1. 考慮「當下圖號」「目的圖號」「上捲或下捲」「圖片總數」「畫格總數」等條件設計演算法。
  2. 定義一個「希望值」:
    1. 希望值就是在轉動停止時所希望停駐的圖號。
    2. 希望值的範圍在0至圖片總數和 –1 之間,若為 –1就表示交由亂數決定。
  3. 計算共要捲動多少張圖片,再換算成多少小格(就是下面程式碼中的「補償列數」),然後再交給轉動程序去執行畫面捲動。
  4. 這部分看似複雜,但基本原理很簡單:
    1. 用「(希望的結果圖號 - 當下圖號)* 單張圖片的高度 」算出要捲動幾小格。
    2. 當「當下圖號」大於「目的圖號」時這個值會是負值,所以再由亂數產生一至數個大週期的格數加上去,以保證傳回的格數不會是負數。
  5. 程序中圖號的邏輯編排分用兩種座標系統,向上捲動時圖號由上至下編排,向下捲動時圖號由下至上編排。
  6. User 認知的圖號是固定不變的,一律是從上至下(0到9)的絶對編號。
  7. 計算應轉動總格數的程式碼如下(L和H表示由亂數決定加上去的大週期數量):

        Function 算出應捲動多少格數(ByRef 希望值, L, H) As Integer
            '------------------------------------------------------------------------------------------
            ' 根據捲動方向校正當下圖號:
            ' 以 User 角度而言圖號為固定編號, 恆為由上而下排列,但在程式中的邏輯編號則受捲動方向支配。
            ' 向上捲動時:圖號由上至下編號(0-n)
            ' 向下捲動時:圖號由下至上編號(n-0)
            '------------------------------------------------------------------------------------------
            If 捲動方向 = 捲動方向列舉常數.向下捲動 Then 當下圖號 = 圖片數 - 當下圖號
            '------------------------------------------------------------------------------------------
            ' 根據希望值計算共要捲動畫格的圖點總列數
            ' 1. 傳入參數 n 為預期的結果圖片號碼
            '    n >= 0:使用者指定期望數字
            '    n <=-1:程式以亂數產生期望數字,做為最後呈現的圖編號。
            ' 2. 以全部圖片的高度總和為一單位,亂數產 3-5 倍的長度做為預設捲動的小格數量。
            ' 3. 若使用者指定期望值,則需計算從結果圖片到目標圖片共需再移動幾小格做為補償格數。
            '------------------------------------------------------------------------------------------
            Randomize()
            If (希望值 < 0) Or (希望值 > 圖片數 - 1) Then 希望值 = Int(Rnd(1) * 圖片數)
            Dim 補償列數 As Integer = 0
            Dim 應捲動總列數 As Integer = _畫格總數 * 亂數產生器.Next(L, H)
            Select Case 捲動方向
                Case 捲動方向列舉常數.向上捲動 : 補償列數 = (希望值 - 當下圖號) * 畫格高
                Case 捲動方向列舉常數.向下捲動 : 補償列數 = (圖片數 - 希望值 - 當下圖號) * 畫格高
            End Select
            應捲動總列數 += 補償列數
            Return 應捲動總列數
        End Function
    


  8. 執行捲動的程式碼如下(等速的捲動):

        '---可指定結果及捲動方向的捲動---
        Sub 捲動圖片2(目的圖框 As PictureBox, Optional 希望值 As Integer = -1)
            Dim 應捲動總列數 = 算出應捲動多少格數(希望值, 1, 1)
            Dim 速度 As Single = 20
            Dim 位移 As Integer = 畫格高 \ 16
            Dim 圖塊大小 As New Rectangle(New Point(0, 0), 目的圖框.Size)
            Dim 當下畫格指標 = 畫格高 * 當下圖號
            Dim ee As Graphics = pic_展示框.CreateGraphics
            Dim 擷圖起點位置 As Integer = 0
            For i As Integer = 當下畫格指標 To 當下畫格指標 + 應捲動總列數 Step 位移
                Select Case 捲動方向           '---根據捲動方向換算擷圖的起點座標---
                    Case 捲動方向列舉常數.向上捲動 : 擷圖起點位置 = i Mod _畫格總數
                    Case 捲動方向列舉常數.向下捲動 : 擷圖起點位置 = _畫格總數 - (i Mod _畫格總數)
                End Select
                ee.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
                ee.DrawImage(接成的長條圖.Image, 圖塊大小, 0, 擷圖起點位置, 畫格寬, 畫格高, GraphicsUnit.Pixel)
                ee.ReleaseHdc(ee.GetHdc)
                Thread.Sleep(速度)
                Application.DoEvents()
            Next
            目的圖框.Image = pic(希望值)
            當下圖號 = 希望值
        End Sub
    


2、要加上運動函數讓轉動顯得自然(起動時由慢至快→然後維持在最高速→停止前由快漸慢到停止)。

  1. 使用二次曲線函數讓效果自然,直接想到的就是重力加速度的 H= 1/2 * G * T^2
  2. 其中的 G 值經測試選擇一個定值,而時間 T 就用已轉動的格數位置代入,指數也不固定用二次方,總之是以實測為準。
  3. 影響轉動速度的因素有兩個,一是每次捲動的格數,一是每次捲動後暫停的的毫秒數。
  4. 這部分的程式碼如下:

        '---加上運動函數的捲動---
        Sub 捲動圖片3(目的圖框 As PictureBox, Optional 希望值 As Integer = -1)
            Dim 已捲動列數 As Integer = 0
            Dim 應捲動總列數 = 算出應捲動多少格數(希望值, 3, 6)   '---根據「當下圖片、期望圖片、捲動方向」算出應捲動格數---
            Dim 位移 = 0                                    '---記錄每次捲動的位移量---
            Dim 擷圖起點位置 As Integer = 0                 '---記錄每次捲動後擷取圖像的 Top 位置---
            Dim 當下暫停毫秒數 As Single = 0
            Dim id As Integer = 0
            加速階段終點列數 = 畫格高 * 2                   '---定義由慢至快的範圍(最前2張圖片)---
            減速階段起點列數 = 應捲動總列數 - 畫格高 * 9    '---定義由快至慢的範圍(最後9張圖片)---
            Try
                Dim 目的框 As New Rectangle(New Point(0, 0), 目的圖框.Size)
                Using ee As Graphics = 目的圖框.CreateGraphics
                    Do
                        id = 畫格高 * 當下圖號 + 已捲動列數
                        Select Case 捲動方向               '---根據捲動方向換算擷圖的起點座標---
                            Case 捲動方向列舉常數.向上捲動 : 擷圖起點位置 = id Mod _畫格總數
                            Case 捲動方向列舉常數.向下捲動 : 擷圖起點位置 = _畫格總數 - (id Mod _畫格總數)
                        End Select
                        ee.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
                        ee.DrawImage(接成的長條圖.Image, 目的框, 0, 擷圖起點位置, 畫格寬, 畫格高, GraphicsUnit.Pixel)
                        ee.ReleaseHdc(ee.GetHdc)
                        已捲動列數 += 位移
                        '---------------------------------------------------------------------
                        ' 速度控制處理(1.起動後加速 2.維持最高速 3.停止前減速)的計算
                        ' 1. 採用基本運動函數(s = a + b * t^2)建立速度曲線。
                        ' 2. 再依當下已捲動列數值傳入到運動函數,以取出暫停毫秒數。
                        '---------------------------------------------------------------------
                        Select Case 已捲動列數
                            Case Is >= 減速階段起點列數 '---停止前減速階段---
                                位移 = 2 + (畫格高 / 4) * ((應捲動總列數 - 已捲動列數) / (應捲動總列數 - 減速階段起點列數)) ^ 3
                                當下暫停毫秒數 = 基本暫停毫秒數 / 2
                            Case Is < 加速階段終點列數  '---啟動後加速階段---
                                位移 = 1 + (畫格高 / 4) * (已捲動列數 / 加速階段終點列數) ^ 1.2
                                當下暫停毫秒數 = 基本暫停毫秒數
                            Case Else                   '---極速階段---
                                位移 = (畫格高 / 2)
                                當下暫停毫秒數 = 基本暫停毫秒數
                        End Select
                        id += 位移
                        Thread.Sleep(當下暫停毫秒數)
                        Application.DoEvents()
                    Loop While 已捲動列數 < 應捲動總列數
                End Using
            Catch ex As Exception
            Finally
                目的圖框.Image = pic(希望值) '---更新控制項的圖片為最後停駐的圖片---
                當下圖號 = 希望值            '---更新當下圖號--- 
                GC.Collect()                 '---清理垃圾---
            End Try
        End Sub
    

 

 

下面是擷圖及影片:

image

 

下載[圖片捲動2_demo.rar

下載[圖片捲動(二)專案原始碼.rar

 

 

 


ku3