使用 RingAllegro 的方块拼图





5.00/5 (3投票s)
使用 RingAllegro 库简化 2D 游戏编程
引言
方块拼图是一款非常知名且流行的游戏,几乎所有人都曾在不同的平台或工具上玩过。我最近玩过的一个例子是 Android 上的“Shuffle 'n Slide 脑力游戏”。这款游戏就是将一张图片切分成 9 块,然后随机打乱在棋盘上,你需要重新排列它们以恢复原始图像。
背景
RingAllegro 是 Ring 编程语言一个非常强大且简单的扩展,它基于 Allegro 库,专门用于轻松流畅地创建具有高交互能力的 2D 游戏,并且是以一种合理的方式来实现 2D 游戏开发创意。
Ring 编程语言是一种最近发布的创新且实用的通用多范式脚本语言,它可以嵌入到 C/C++ 项目中,使用 C/C++ 代码进行扩展,以及/或作为独立语言使用。支持的编程范式包括命令式、过程式、面向对象、函数式、元编程、声明式编程(使用嵌套结构)和自然编程。该语言是可移植的(Windows、Linux、Mac OS X、Android 等),可用于创建控制台、GUI、Web、游戏和移动应用程序。该语言的设计宗旨是简洁、小巧、灵活和快速。它是一种动态语言(动态类型和弱类型),它将源代码编译成字节码,然后由 Ring 虚拟机执行,Ring 虚拟机集成在 Ring 编译器中,构成一个程序。
Ring 中的编码原则
Ring 编程语言中的编码过程有其独特的原则,我们必须遵循,这些原则是为了实现不同程序员和开发者之间的高度编程便利性,并与其多范式理念轻松协同工作。现在,我需要展示其中一些原则,它们对于更顺畅的阅读至关重要。
Ring 编程语言不认为 Main
方法是必需的,因此编写的代码将从第一行执行到最后一行。在这种情况下,所有代码部分都应按照以下顺序进行特殊组织:
- 文件加载
- 语句和全局变量
- 函数
- 包和类
* 关于 Ring 编码,我们还需要考虑的其他事项包括:
- 不区分大小写
- 语句没有显式结束符(不需要 ; 或 ENTER)
- 函数调用可以先于定义
- 没有关键字来结束函数/类/包
代码概述
如果您对 RingAllegro 和 Allegro 库函数 有一定了解,那么代码基本上是自解释的,因此我将尝试通过一些注释来介绍其主要部分。
初始化和棋盘准备
与许多编程语言一样,Ring 编程语言的代码以包含(inclusions)开始,然后是变量定义。
Load "gamelib.ring"
BoardDim = 398 # Board dimension
SquarePostions = [[0,0],2,3,4,5,6,7,8,9] # Each position contain x,y
SquareDim = 0 # Square dimension
Moves = 0 # To count number of moves as to estimate the level of solving strategies
Squares = ["square",1] # Each square combined with its current position during playing
然后,我们需要初始化 RingAllegro
库,并创建/准备将包含游戏棋盘、分割的图像以及棋盘上方和下方标签的主窗口。
# Initialization ===========
al_init()
al_init_image_addon()
al_init_font_addon()
al_init_ttf_addon()
font = al_load_ttf_font("pirulen.ttf",14,0 )
display = al_create_display(406,500)
al_clear_to_color(al_map_rgb(255,255,255))
Board = al_create_bitmap(boarddim,boarddim)
al_set_target_bitmap(board)
al_clear_to_color(al_map_rgb(220,220,220))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(board,al_get_display_width(display) / 2 - boarddim /2,
al_get_display_height(display) / 2 - boarddim / 2,0)
Image = al_create_sub_bitmap(al_load_bitmap("palace.jpg"),0,0,boarddim - 8 ,boarddim - 8)
MoveLabel = al_create_bitmap(al_get_display_width(display),30)
al_set_target_bitmap(movelabel)
al_clear_to_color(al_map_rgb(220,220,220))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_text(font, al_map_rgb(0,0,0), 150, 20,ALLEGRO_ALIGN_LEFT, "Squares :)")
al_flip_display() # This function is very important after each change to be displayed
然后,我们需要初始化 Allegro 库的事件监听机制,它由事件队列和输入注册组成,如下所示:
# Events Initialization ===============================
event_queue = al_create_event_queue()
ev = al_new_allegro_event()
al_register_event_source(event_queue, al_get_display_event_source(display))
al_install_mouse()
al_register_event_source(event_queue, al_get_mouse_event_source())
al_install_keyboard()
al_register_event_source(event_queue, al_get_keyboard_event_source())
然后,我们需要确定每个方块应该占据的确切 x,y 位置,接着从整个图片中分割出方块。
# SetSquaresPositionsAndDimensions =======================
squaredim = (boarddim - 8) / 3
boardx = al_get_display_width(display) /2 - boarddim /2
boardy = al_get_display_height(display) /2 - boarddim /2
SquarePostions[1] = [boardx +2 , boardy +2]
SquarePostions[2] = [boardx + squaredim + 4, boardy +2]
SquarePostions[3] = [boardx + squaredim * 2 + 6, boardy +2]
SquarePostions[4] = [boardx +2 , boardy + squaredim + 4]
SquarePostions[5] = [boardx + squaredim + 4, boardy + squaredim + 4]
SquarePostions[6] = [boardx + squaredim * 2 + 6, boardy + squaredim + 4]
SquarePostions[7] = [boardx +2 , boardy + squaredim * 2 + 6]
SquarePostions[8] = [boardx + squaredim + 4, boardy + squaredim * 2 + 6]
SquarePostions[9] = [boardx + squaredim * 2 + 6, boardy + squaredim * 2 + 6]
# CreateSquares ==========================
squares = list(9)
ind = 1
for j = 0 to 2
for i = 0 to 2
squares[ind] = [al_create_sub_bitmap
(image,i * squaredim, j * squaredim,squaredim,squaredim),-1]
ind += 1
next
next
squares[9] = [al_create_sub_bitmap(board,1,1,squaredim,squaredim),-1]
游戏和交互
在开始游戏挑战之前,我们需要给玩家一些机会,以便更容易地制定解决策略。
# Draw full Pic with timer ===========
al_draw_bitmap(image,al_get_display_width(display) /2 - boarddim /2 + 4,
al_get_display_height(display) /2 - boarddim /2 + 4,0)
al_flip_display()
timer = al_create_timer(1)
al_register_event_source(event_queue, al_get_timer_event_source(timer))
al_start_timer(timer)
moves = 3
while true
al_wait_for_event(event_queue, ev)
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 200, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, string(moves))
moves -= 1
al_flip_display()
if moves = -1 moves = 0 exit ok
end
al_destroy_timer(timer)
al_rest(1)
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 150, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, "Moves : " + moves)
al_flip_display()
现在是时候通过随机化方块位置并将它们绘制在棋盘上来让玩家参与挑战了。
# RandomizePositions ================================
for i = 1 to 9
while true
Ind = LimitRandom(1,9)
found = false
for s in squares
if ind = s[2] found = true ok
next
if found = false squares[i][2] = ind exit ok
end
next
# DrawRandomizedSquares ===============
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(board,al_get_display_width(display) / 2 - boarddim /2,
al_get_display_height(display) / 2 - boarddim / 2,0)
for s in squares
al_draw_bitmap(s[1],SquarePostions[s[2]][1],SquarePostions[s[2]][2],0)
next
al_flip_display()
现在是时候让玩家施展浑身解数了。:)
这可以通过一个 while
循环来实现,该循环包括监听玩家的交互,应用玩家选择的移动,然后检查整个谜题是否已正确解决。
当玩家成功完成谜题时,我们需要根据他/她使用的移动次数来评估和评价他/她的解谜努力。
while true
al_wait_for_event(event_queue, ev)
switch al_get_allegro_event_type(ev)
on ALLEGRO_EVENT_DISPLAY_CLOSE
exit
on ALLEGRO_EVENT_MOUSE_BUTTON_UP
mouse_x = al_get_allegro_event_mouse_x(ev)
mouse_y = al_get_allegro_event_mouse_y(ev)
for cursqr = 1 to 9
dx = mouse_x - SquarePostions[cursqr][1]
dy = mouse_y - SquarePostions[cursqr][2]
if dx < squaredim and dx > 0
if dy < squaredim and dy > 0
ns = CheckNearSpace(cursqr)
if ns != 0 exsquareposition(cursqr,ns)
ok
ok
ok
next
on ALLEGRO_EVENT_KEY_UP
switch al_get_allegro_event_keyboard_keycode(ev)
on ALLEGRO_KEY_UP
ps = GiveSelectedSqrInd(1)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_DOWN
ps = GiveSelectedSqrInd(3)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_LEFT
ps = GiveSelectedSqrInd(4)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
on ALLEGRO_KEY_RIGHT
ps = GiveSelectedSqrInd(2)
if ps != 0 exsquareposition(ps,squares[9][2]) ok
off
off
if Solved() = true
ft = al_load_ttf_font("pirulen.ttf",20,0 )
lastpanel = al_create_bitmap(350,70)
al_set_target_bitmap(lastpanel)
al_clear_to_color(al_map_rgb(255,255,255))
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(lastpanel,al_get_display_width(display) /2 - boarddim /2 + 24,
al_get_display_height(display) /2 -30,0)
if moves < 50
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 80, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Really Genius")
but moves >= 50 and moves < 100
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 100, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Really Smart")
but moves >= 100 and moves < 200
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 100, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "You Are Smart")
but moves >= 200
al_draw_text(ft, al_map_rgb(0,0,0), 40, al_get_display_height(display) /2 - 20,
ALLEGRO_ALIGN_LEFT, "Congratulations ^_^")
al_draw_text(font, al_map_rgb(0,0,0), 90, al_get_display_height(display) /2,
ALLEGRO_ALIGN_LEFT, "But Slowly Solved")
ok
al_flip_display()
al_rest(15)
exit
ok
end
收尾和函数
解完谜题后,我们需要收尾并销毁之前使用的 RingAllegro
组件,如下所示:
# Finalization =================
al_destroy_event_queue(event_queue)
al_destroy_allegro_event(ev)
for s in squares
al_destroy_bitmap(s[1])
next
al_destroy_bitmap(image)
al_destroy_bitmap(board)
al_destroy_display(display)
al_exit()
最后,我们已经完成了执行这个简单游戏的主要代码,但我们未讨论的其余代码行是用于在游戏中本地定义和使用的函数,因为在 Ring 语言中,在代码底部声明类和函数的规则。
func LimitRandom S,E
while true
r = random(e)
if r >= s return r ok
end
func ExSquarePosition filled,spaced
al_set_target_bitmap(al_get_backbuffer(display))
al_draw_bitmap(squares[find(squares,filled,2)][1],
SquarePostions[squares[9][2]][1],SquarePostions[squares[9][2]][2],0)
al_draw_bitmap(squares[9][1],SquarePostions[filled][1],SquarePostions[filled][2],0)
squares[9][2] = filled
squares[find(squares,filled,2)][2] = spaced
moves += 1
al_draw_bitmap(movelabel,0,al_get_display_height(display) - 30,0)
al_draw_text(font, al_map_rgb(0,0,0), 150, al_get_display_height(display) - 20,
ALLEGRO_ALIGN_LEFT, "Moves : " + moves)
al_flip_display()
func CheckNearSpace i
S = squares[9][2]
switch i
on 1
switch s on 2 return s on 4 return s off
on 2
switch s on 1 return s on 3 return s on 5 return s off
on 3
switch s on 2 return s on 6 return s off
on 4
switch s on 1 return s on 5 return s on 7 return s off
on 5
switch s on 2 return s on 4 return s on 6 return s on 8 return s off
on 6
switch s on 3 return s on 5 return s on 9 return s off
on 7
switch s on 4 return s on 8 return s off
on 8
switch s on 5 return s on 7 return s on 9 return s off
on 9
switch s on 6 return s on 8 return s off
off
return 0
func GiveSelectedSqrInd Dir # Up 1, Right 2, Down 3, Left 4
S = squares[9][2]
switch s
on 1
switch dir on 1 return 4 on 4 return 2 off
on 2
switch dir on 1 return 5 on 2 return 1 on 4 return 3 off
on 3
switch dir on 1 return 6 on 2 return 2 off
on 4
switch dir on 1 return 7 on 4 return 5 on 3 return 1 off
on 5
switch dir on 1 return 8 on 2 return 4 on 3 return 2 on 4 return 6 off
on 6
switch dir on 1 return 9 on 2 return 5 on 3 return 3 off
on 7
switch dir on 3 return 4 on 4 return 8 off
on 8
switch dir on 2 return 7 on 3 return 5 on 4 return 9 off
on 9
switch dir on 2 return 8 on 3 return 6 off
off
return 0
func Solved
for i = 1 to 9
if squares[i][2] != i return false ok
next
return true
关注点
我需要在此声明的主要关注点是使用 Ring 编程语言进行编码的轻松性和简单性,因为我不必担心应使用何种类型的变量,也无需担心数组的类型、大小甚至维度。
另一个值得关注的方面是,这款游戏可以跨平台玩,我可以非常轻松地将其移植到其他操作系统。
我想强调的最后一件事是,Allegro 库在编写简单游戏方面有一种非常好的创新方式,它已经提供了清晰预定义的函数,因此我认为我真的很幸运能发现它已经与 Ring 编程语言集成。
资源
历史
- 2016 年 4 月 30 日:初始版本