Xây dựng tín hiệu đầu vào

Một phần của tài liệu (LUẬN văn THẠC sĩ) nghiên cứu lập trình phản ứng với ngôn ngữ ELM (Trang 64)

3.3 Xây dựng code chƣơng trình với ngôn ngữ Elm

3.3.3 Xây dựng tín hiệu đầu vào

Trong chƣơng trình hàm thời gian sẽ đƣợc định nghĩa bởi hàm fps: 169 fps : number -> Signal Time

170 delta = Signal.map inSeconds (fps 35)

Hàm sẽ thực hiện một số lƣợng khung hình trong mỗi giây và kết quả tín hiệu sẽ đƣợc trả lại nhanh nhất có thể. Chức năng của nó làm cho các đối tƣợng di chuyển mƣợt và động hơn.

Tín hiệu đầu vào Input gồm phím Space, phím Enter, phím shift, phím “N”, các phím mũi tên (Arrows), các phím (w, a, s, d) và cả tín hiệu thời gian (Time). Để nối tất cả chúng thành một dòng tín hiệu chung ta sử dụng hàm (<~, ~) các hàm này tƣơng đƣơng với hàm map.

158 input : Signal Input

159 input = sampleOn delta <| (Input

160 <~ Keyboard.isDown (Char.toCode 'N') 161 ~ Keyboard.space 162 ~ Keyboard.enter 163 ~ Keyboard.shift 164 ~ Keyboard.arrows 165 ~ Keyboard.wasd 166 ~ delta)

Hàm sampleOn nhận hai tín hiệu đầu vào và trả lại tín hiệu sau dựa trên tín hiệu trƣớc đó. Ở đoạn mã trên giá trị trả về là một trong các tín hiệu của dòng dựa trên giá trị thời gian.

3.3.4 Cập nhật dữ liệu chương trình

Một chƣơng trình game sẽ có các đối tƣợng tƣơng tác với nhau vì vậy ta sẽ viết trƣớc các hàm xử lý các sự kiện khi chúng va chạm với nhau.

78 near : number -> number -> number -> Bool 79 near k c n =

80 n >= k-c && n <= k+c

Hàm near là hàm kiểm tra khoảng cách giữa các đối tƣợng xem chúng có gần nhau hay không và nếu gần nhau thì sẽ thực thị một sự kiện nào đó.

82 Within : Ball -> Player -> Bool 83 within ball player =

84 near player.x 40 ball.x && near player.y 40 ball.y

Hàm within kiểm tra sự đụng độ xem quả bóng có gần cầu thủ không. 72 stepv : Float -> Bool -> Bool -> Float

73 stepv v bottom up =

74 if | bottom -> abs v 75 | up -> -(abs v)

76 | otherwise -> v

Hàm stepv thay đổi vận tốc thông qua sự kiện va chạm giữa bóng với các cạnh biên của sân bóng. Nếu bóng va trạm với biên trên thì sẽ thay đổi với hàm trị tuyệt đối (abs v) và ngƣợc lại nếu va chạm biên dƣới thì vận tốc sẽ có giá trị phủ định với hàm -(abs v). Nếu không có sự va chạm bóng sẽ giữ vận tốc di chuyển mà không có sự thay đổi. Quá trình cập nhật vị trí và vận tốc quả bóng sẽ đƣợc lập đi lập lại. 118 distance t ({x,y,vx,vy} as obj) = 119 { obj | 120 x <- x + vx * t, 121 y <- y + vy * t 122 }

Hàm distance cập nhật khoảng cách di chuyển của các đối tƣợng với một khoảng thời gian t. Vị trí mới của đối tƣợng sẽ đƣợc cập nhật bằng việc tọa độ cũ cộng với vận tốc nhân với thời gian.

Tiếp theo ta xét hàm updateBall cập nhật dữ liệu của quả bóng khi di chuyển và phản ứng với các đối tƣợng trong chƣơng trình.

86 updateBall : Time -> Ball -> Player -> Player -> Ball 87 updateBall t ({x,y,vx,vy} as ball) p1 p2 =

88 if (ball `within` p1) && ball.shoot1 == False then 89 {ball |

90 vx <- p1.vx, 91 vy <- p1.vy,

92 x <- p1.x, y <- p1.y, shoot2 <- False}

93 else if (ball `within` p2) && ball.shoot2 == False then 94 {ball |

96 vy <- p2.vy,

97 x <- p2.x, y <- p2.y, shoot1 <- False}

98 else if not (ball.x |> near 0 halfWidth) then

99 {ball | x <- 0, y <- 0, shoot1 <- False, shoot2 <- False}

Khi bóng di chuyển gập cầu thủ 1 nó và trạng thái shoot1 == False tức là bóng sẽ không đƣợc sút và bóng sẽ di chuyển theo cầu thủ đó. Tƣơng tự với cầu thủ 2 cũng vậy. Nhƣng nếu bóng không nằm trong khoảng ½ sân bóng (halfWidth) thì tọa độ quả bóng sẽ quay về vị trí (0, 0) việc kiểm tra khoảng cách này thực hiện bằng hàm near. Nếu quả bóng nằm trong sân thì vị trí tọa độ và vận tốc quả bóng sẽ đƣợc cập nhật. Xét quá trình cập nhật vận tốc vx có điều kiện (y > goal.h/2 || y < -goal.h/2) thì giá trị của quả bóng sẽ đƣợc cập nhật tiếp, ngƣợc lại quả bóng sẽ bay vào khung thành và quay lại vị trí (0, 0).

Xét tiếp quá trình hoạt động của quả bóng. 100 else if ball.shoot1 == True then 101 distance t

102 { ball |

103 vx <- stepv vx (x < 20 - halfWidth && (y > goal.h/2 || y < -

goal.h/2)) True,

104 vy <- stepv vy (y < 20 - halfHeight) (y > halfHeight - 20),

105 shoot1 <- if (x <= -halfWidth + 20 && (y > goal.h/2 || y < -

goal.h/2)) || (x >= halfWidth - 20 && (y > goal.h/2 || y < -goal.h/2))

then False else True,

106 shoot2 <- False 107 }

108 else if ball.shoot2 == True then distance t 109 { ball |

110 vx <- stepv vx (x < 20 - halfWidth && (y > goal.h/2 || y < -

goal.h/2)) False,

112 vy <- stepv vy (y < 20 - halfHeight) (y > halfHeight - 20),

113 shoot1 <- False,

114 shoot2 <- if (x <= -halfWidth + 20 && (y > goal.h/2 || y < -

goal.h/2)) || (x >= halfWidth - 20 && (y > goal.h/2 || y < -goal.h/2))

then False else True

115 }

Nếu nhƣ quà bóng đƣợc sút bởi cầu thủ ball.shoot1 == True hoặc ball.shoot2 == True thì tọa độ và vận tốc bóng lại đƣợc thay đổi.

116 else distance t 117 { ball |

118 vx <- stepv vx (x <= 20 - halfWidth && (y > goal.h/2 || y < -goal.h/2)) (x >= halfWidth - 20 && (y > goal.h/2 || y < -goal.h/2)),

119 vy <- stepv vy (y < 20 - halfHeight) (y > halfHeight - 20)

120 }

Nếu nhƣ bóng không chạm cầu thủ nào nó sẽ thay đổi vận tốc vx, vy nếu trạm vào các cạnh biên của sân và giữ nguyên vận tốc nếu không chạm vào đâu. Nếu bóng rơi vào tọa độ (y < goal.h/2 || y > -goal.h/2) thì bóng sẽ nằm trong khung thành và đƣợc cập nhật về tọa đô (0, 0).

Xét việc di chuyển của cầu thủ 2 đội chơi thông qua hàm updatePlayer. Hàm này đƣợc khai báo với các tham số nhƣ thời gian (Time), phím nhập vào từ bàn phím (keys), điểm của ngƣời chơi (points) và ngƣời chơi đang cầm bóng ( currentPlayer) và cầu thủ còn lại.

124 updatePlayer: Time -> Keys -> Int -> Player -> Player -> Player 125 updatePlayer t keys points currentPlayer player

Khi 2 cầu thủ di chuyển mà va chạm với nhau thì sẽ không cho vƣợt qua và bắt buộc phải di chuyển hƣớng khác. Quá trình này sẽ đƣợc kiểm tra bằng hàm near.

127 if (near currentPlayer.x 40 player.x && near currentPlayer.y 40

player.y )then

128 distance t {currentPlayer|

129 x <- if | (currentPlayer.dir == Left && keys.x < 0) ->

currentPlayer.x + 40

130 | (currentPlayer.dir == Right && keys.x > 0) ->

currentPlayer.x - 40

131 | otherwise -> currentPlayer.x,

132 y <- if | (currentPlayer.dir == Up && keys.y > 0) ->

currentPlayer.y - 40

133 | (currentPlayer.dir == Down && keys.y < 0) ->

currentPlayer.y + 40

134 | otherwise -> currentPlayer.y 135 }

Hàm near sẽ kiểm tra xem nếu hai cầu thủ cách nhau một khoảng x = 40 và y = 40 thì ngƣời chơi sẽ bị đẩy văng ra một khoảng là 40. Còn nếu không có va

chạm thì các cầu thủ sẽ đƣợc cập nhật tọa độ và khoảng cách, hƣớng di chuyển nhƣ đoạn mã dƣới đây.

139 else 140 distance t { currentPlayer| 141 vx <- toFloat (keys.x)*200, 142 vy <- toFloat (keys.y)*200, 143 dir <- 144 if | keys.x > 0 -> Right 145 | keys.x < 0 -> Left 146 | keys.y < 0 -> Down 147 | keys.y > 0 -> Up

148 | otherwise -> currentPlayer.dir 149 }

Trong biểu thức inta có hàm clamp giới hạn tọa độ của cầu thủ với biên của sân sân bóng và không cho ra khỏi sân bóng. Việc cập nhật điểm của đội chơi cũng sẽ đƣợc cập nhật nếu nhƣ cầu thủ của đội nào ghi bàn thắng.

151 { p |

152 x <- clamp (20 - halfWidth) (halfWidth - 20) p.x,

153 y <- clamp (20 - halfHeight) (halfHeight - 20) p.y,

154 score <- currentPlayer.score + points 155 }

Sau khi viết xong các hàm cập nhật các đối tƣợng ta sẽ gộp tất cả lại thành một hàm cập nhật dữ liệu chung gồm cả các tín hiệu đầu vào từ bàn phím. Hàm update sẽ bao gồm các tính hiệu đầu vào, các đối tƣợng và cả trạng thái của trò chơi.

33 update: Input -> Data -> Data

34 update{isDown,space,enter,shift,dir1,dir2,delta} ({state,ball,img1,img2} as game) =

35 let 36 score1 =

37 if ball.x > halfWidth then 1 else 0

38 score2 =

39 if ball.x < -halfWidth then 1 else 0

41 newState =

43 if img1.score >= gameOver || img2.score >= gameOver then Pause

44 else Play

45 | score1 /= score2 -> Pause 46 | otherwise -> state

48 newBall =

49 if state == Pause then 50 ball

51 else updateBall delta ball img1 img2

Trong hàm ta tiếp tục sử dụng biểu thức let để định nghĩa hàm và giá trị của các đối tƣợng. Hàm Score1 và Score2 thực hiện việc tính điểm cho đội chơi nếu nhƣ quả bóng vào khung thành. Hàm newState thực hiện trạng thái của trò chơi, nếu nhƣ bấm phím space thì tiếp tục chơi với điều kiện điểm của 2 đội chơi không quá 5 điểm. Còn nếu một trong 2 đội chơi quá 5 điểm thì trò chơi sẽ kết thúc. Hàm newBall sẽ cập nhật giá trị quả bóng nếu trong trạng thái đang chơi.

Ta xét tiếp quá trình xử lý trong biểu thức in chức năng sẽ cập nhật tiếp các giá trị của các đối tƣợng cũng nhƣ trạng thái trò chơi.

52 in

53 if isDown then defaultGame 54 else if enter then

55 {game |

56 ball <- Ball ball.x ball.y 200 200 (if near img1.x 10

ball.x then True else False) False 57 }

58 else if shift then 59 {game |

60 ball <- Ball ball.x ball.y 200 200 False (if near img2.x

10 ball.x then True else False)

61 } 62 else 63 {game |

64 state <- newState,

65 ball <- newBall,

66 img1 <- updatePlayer delta dir1 score1 img1 img2,

67 img2 <- updatePlayer delta dir2 score2 img2 img1 68 }

Với tín hiệu nhập vào là enter thì bóng sẽ cầu thủ một đƣợc sút đi, nếu tín hiệu là shift thì bóng đƣợc cầu thủ 2 sút đi. Với tín hiệu nhập phím là isDown ở trong chƣơng trình là phím “N”: Keyboard.isDown (Char.toCode 'N') thì khởi động lại trò chơi đƣa các giá trị về trạng thái ban đầu.

3.3.5 Thực thi và chạy chương trình

Hàm main sẽ thực thi và hiển thị chƣơng trình ra ngoài màn hình. 269 main = Signal.map2 view Window.dimensions

(Debug.watch "Xem toan bo" <~ gameState)

270 gameState : Signal Data

271 gameState = Signal.foldp update defaultGame input

Hàm gameState dùng để thực hiện việc lƣu trạng thái trò chơi thông qua hàm foldp. Hàm này sẽ cập nhật dữ liệu vào defaultGame ở mọi thời điểm khi tín hiệu đƣợc nhập vào.

Hàm Debug.watch cho ta xem đƣợc tất cả các giá trị của dữ liệu chƣơng trình thay đổi theo thời gian và có thể tua lại một cách chính xác các phản ứng sự kiện diễn ra trong quá khứ.

Chạy ứng dụng

Sau khi ứng dụng đƣợc xây dựng xong, tôi sẽ biên dịch nó ra phai html và đƣa lên mạng để mọi ngƣời cùng chơi. Câu lệnh biên dịch sang tập tin html nhƣ sau:

elm make game4Hockey.elm --output hockey.html

3.4 Nhận xét, đánh giá và thảo luận

3.4.1 Ưu điểm của lập trình phản ứng

Trong xu hƣớng công nghệ hiện nay thì lập trình phản ứng là công nghệ mới đang đƣợc phát triển rất mạnh trong các lĩnh vực mà dữ liệu chƣơng trình rất lớn hay là các ứng dụng web, ứng di động có tính tƣơng tác cao với ngƣời dùng ở thời gian thực. Để thấy rõ đƣợc tầm quan trọng này trong luận văn này tôi đƣa ra một số mặt mạnh của lập trình phản ứng nhƣ sau:

Trong lập trình phản ứng luôn mang tính hƣớng sự kiện và các sự kiện có thể xảy bất cứ lúc nào mà không cần phải tuân theo thứ tự các sự kiện đã đƣợc sắp đặt từ trƣớc. Sức mạnh của lập trình phản ứng là có thể nhập, lọc và nối nhiều sự kiện lại với nhau thành một dòng trƣớc khi xử lý tiếp các sự kiện phía bên trong.

Lập trình phản ứng cho phép chúng ta khai báo thiết lập các rằng buộc giữa các dữ liệu trong các phần khác nhau của chƣơng trình và tạo một cơ chế tự động đồng bộ hóa giữa các sự kiện. Lập trình hàm phản ứng có thể cho phép trừu tƣợng hóa thông tin có thể thay đổi theo thời gian. Hầu hết các chƣơng trình lập trình phản ứng sẽ đáp ứng ở thời gian thực, hiệu quả hoạt động và tối ƣu hóa khi thực thi là rất cao.

Phần năng động nhất của trong lập trình phản ứng chính là giao diện tƣơng tác ngƣời dùng, nó vừa là kết nối với mô hình dữ liệu phía bên trong vừa kết nối với những tác động từ bên ngoài vào, cũng đồng cũng thể hiện đƣợc sự thay đổi dữ liệu bên trong thông qua giao diện mà chúng ta có thể quan sát. Vì vậy lựa chọn các ngôn ngữ thể hiện đƣợc giao diện đồ họa cao thì cũng thể hiện khá tốt lập trình

phản ứng trong nó. Thông dụng nhất hiện nay vẫn là ngôn ngữ Javascript và ngôn ngữ Elm.

Tuy lập trình phản ứng là một mô hình lập trình mới mang tính hƣớng sự kiện nhƣng nó vẫn mang phong cách của lập trình hàm và lập trình hƣớng đối tƣợng. Vì vậy với một ngƣời lập trình mới bắt đầu làm quen với mô hình này cũng dễ dàng tiếp cập và tìm hiểu sâu hơn về nó.

3.4.2 Một số khó khăn của lập trình phản ứng

Chúng ta đều biết tƣ duy trong lập trình phản ứng là việc truyển đổi các dữ liệu đầu vào từ môi trƣờng bên ngoài tác động vào dữ liệu bên trong chƣơng trình để thay đổi theo thời gian. Vì vậy cái khó của ngƣời lập trình là làm cách nào để biểu diễn dữ liệu thay đổi liên tục theo thời gian khi có tác động từ bên ngoài vào.

Việc mô hình hóa dữ liệu thay đổi theo thời gian thì trong lập trình phản ứng sử dụng khái niệm đó là tín hiệu. Nhƣng các tín hiệu đầu vào tƣơng tác với chƣơng trình ở các thời điểm khác nhau không đoán trƣớc đƣợc thời điểm tác động vào dẫn đến các việc cập nhật sự kiện khá khó khăn và không giữ đúng thứ tự các sự kiện đã đƣợc sắp đặt trƣớc dẫn đến việc các luồng chảy sự kiện trong chƣơng trình ngày càng trở nên khó hiểu.

Trong cùng thời điểm các sự kiện diễn ra trong một thời gian dài sẽ đƣợc xử lý rất lâu khiến các sự kiện nhỏ phải đợi. Vì vậy việc sắp sếp thứ tự sự kiện trong lập trình phản ứng cũng là một vấn đề đặc biệt là với các sự kiện không liên quan tới quá trình xử lý.

Trong lập trình phản ứng việc tạo ra một tín hiệu có thể cần vòng lặp vô hạn sẽ dẫn đến việc bộ nhớ sẽ phát triển tuyến tính theo thời gian đồng nghĩa với việc rất tốn bộ nhớ.

Lập trình phản ứng cho phép định nghĩa các giá trị phụ thuộc vào quá khứ, hiện tại và tƣơng lai tức là chƣơng trình tạo giá trị mới từ các giá trị cũ. Việc dựa trên những giá trị quá khứ có thể tăng bộ nhớ cái mà chƣơng trình bắt buộc nhớ các

giá trị trong quá khứ của hành vi hoặc sự kiện. Có nghĩa là sử dụng bộ nhớ tăng tỷ lệ thuận với thời gian mà chƣơng trình chạy.

3.4.3 Ưu điểm của ngôn ngữ Elm

Giao diện đồ họa ngƣời dùng ngƣời dùng là hệ trung gian cho nhiều giao tiếp của chúng ta với máy tính vì vậy lập trình hàm phản ứng với ngôn ngữ Elm là một phƣơng pháp tiếp cận nhanh nhất để thiết kế giao diện đồ họa ngƣời dùng, cung cấp một cái nhìn cao hơn, sâu hơn, trừu tƣợng hơn để miêu tả sự tƣơng tác của ngƣời dùng và các tính toán phụ thuộc về thời gian.

Elm là một ngôn ngữ lập trình hàm phản ứng mang tính hƣớng sự kiện hoạt động chủ yếu dựa vào tín hiệu cái mà giá trị luôn thay đổi theo thời gian, tín hiệu luôn tác động vào dữ liệu chƣơng trình và làm chúng thay đổi khi có sự kiện xảy ra.

Một phần của tài liệu (LUẬN văn THẠC sĩ) nghiên cứu lập trình phản ứng với ngôn ngữ ELM (Trang 64)

Tải bản đầy đủ (PDF)

(79 trang)