前言
第一次使用 PICO-8 开发游戏,虽然跟 Unity3d,cocos2d 比起来 PICO-8实在是简陋,但是用起来却十分的令人着迷。8bit 复古风的 PICO-8 的限制十分的多,比如 128x128 的分辨率,只有 16 种颜色,可以使用的 sprite 和地图的大小也有限制。但是在这种限制下反而可以更简单的去考虑游戏本身的玩法,而且 PICO-8 的分享修改机制可以快速的修改他人的游戏,尝试不同的玩法。总之 PICO-8 是一个可以用来快速实现并检验游戏创意的游戏引擎,对于想尝试开发游戏的新手也十分友好,仅凭官方文档中的简单介绍就可以立刻上手。下面我就分享下开发过程,先上下游戏截图:
在线版本地址:
https://www.lexaloffle.com/bbs/?pid=54230#p54229
indienova PICO-8 在线玩:
使用其他代码编辑器
打开 PICO-8 未进入任何游戏之前是默认的空项目,点击 ESC 切换到编辑模式可以看到,代码 / sprite / map / 音效 / 音乐编辑器。
自带的编辑器只有大写使用起来十分不便,我采用自己的熟悉的编辑器(SublimeText)来进行代码编辑。
使用其他编辑器之前首先保存项目,ESC 切换到控制台下,使用 save loop.p8
命令进行保存文件。保存后我们可以在 PICO-8 目录中找到保存的 `loop.p8`文件,不同的系统的默认路径不同:
Windows: C:/Users/Yourname/AppData/Roaming/pico-8/carts OSX: /Users/Yourname/Library/Application Support/pico-8/carts Linux: ~/.lexaloffle/pico-8/carts
找到 p8文件后直接拖入想使用的文本编辑器即可,在外部编辑器编辑完成保存后切回 PICO-8后,使用 CTRL+R 刷新即可看到结果(但有的时候代码错误,会导致刷新没有效果,这时候回到控制台模式输入 load loop.p8 重新载入后再输入 run 命令重新运行)。
代码结构
PICO-8提供了3个特殊函数,只要代码中有这三个函数引擎会自动调用以完成游戏循环
_update() 更新函数,以 30fps 的速度执行,即每 0.033s 执行一次 _draw() 绘制函数每次 update 执行一次 _init() 初始化函数,游戏启动时执行一次
结构十分简单,在 update 中处理用户输入等变量,在 draw 中根据变量进行绘制
绘制 cursor
我们需要一个 cursor 根据玩家输入进行移动,首先在 PICO-8的编辑模式中绘制 cursor。PICO-8中的 sprite 大小是 8x8 像素,我们需要的的 cursor 大小是 16x16,可以将上图红框中的范围拖到第二格,就可以直接绘制 16x16 的范围了。
绘制好了之后就可以在 _draw()
中使用了,PICO-8 提供了 spr
函数将 sprite sheet 中的图片绘制到游戏中,[ ]
中的参数为可选参数:
spr n x y [w h] [flip_x] [flip_y] n: sprite 的序号,可以在编辑模式中查看 x y: 绘制到屏幕上的坐标 w h:绘制到屏幕上的宽高默认1 1 flip_x flip_y:水平/垂直方向是否翻转
spr
一次只能绘制一个 8x8 的 sprite 而光标是16x16 需要执行 4 次才可以绘制完成,好在还有另一个更方便的函数 sspr
sspr sx sy sw sh dx dy [dw dh] [flip_x] [flip_y]
跟 spr
不同的是 sspr
不使用 sprite 序号定位,而是使用 sx sy sw sh
;这 4 个参数代表在 sprite sheet 中的 sprite 的 左上角坐标 x,y 和 sprite 的宽高,对于上图中的 cursor 这 4 个参数应该是 8,0,16,16
;dx dy dw dh
是绘制到屏幕上的坐标,dw dh
不输入默认取 sw sh
。
sx sy sw sh
这四个值是固定的,我们只需要根据用户输入在 update
中调整 dx dy 这两个值就可以移动 cursor 了。为了在 draw 和 update 都可以调用位置信息,用一个全局变量储存 cursor 的信息。
cursor = {} cursor.x = 0 cursor.y = 0 function _draw() cls(1) sspr(8,0,16,16,cursor.x*16,cursor.y*16) end function _update() if btnp(0,0) then cursor.x -= 1 end if btnp(1,0) then cursor.x += 1 end if btnp(2,0) then cursor.y -= 1 end if btnp(3,0) then cursor.y += 1 end if cursor.x < 0 then cursor.x += 8 end if cursor.x > 7 then cursor.x -= 8 end if cursor.y < 0 then cursor.y += 8 end if cursor.y > 7 then cursor.y -= 8 end end
cursor.x
cursor.y
存的不是坐标值,而是 cursor 当前所在格子的位置,这样每次移动 cursor 都会移动一格而不是一个像素。屏幕尺寸是 128x128 ,格子的大小也就是 cursor 的大小是 16x16 所以格子的尺寸应该是 8x8,x,y
取值[0-7]
。_update
函数中使用btnp
来获取被按下的按键,btnp
两个参数第一个参数是按钮代码,二个参数是玩家代码,取0
就是第一个玩家。按键代码0123
分别是左右上下。- 当按钮按下时移动 cursor 的
x,y
当到达屏幕边缘时,比如x<0
将其+8
移动到最右边。 _draw
函数中首先调用cls
函数清空屏幕以便重新绘制,cls
有一个可选参数表示使用指定颜色进行填充屏幕,从 16 个颜色中选。
使用同样的办法可以将游戏中的一个个旋转的图形绘制出来,这里遇到个问题,我没有找到旋转的函数。PICO-8 只提供了 flip_x
flip_y
进行翻转,有些旋转可以通过翻转代替,有些就没法做到了。所以有些图形我画了两个,这样就可以通过水平和垂直翻转实现 4 个方向旋转了。
关于游戏数据,我使用了 2 个数组,一个用来存图形的类型(一共 5 种)一个用来存储旋转方向,这两个数组长度都是 64(8*8)。关于游戏数据生成,如何判断胜利的算法,代码比较长这里就不多说了,可以直接看代码。点击上面的在线版链接,在 code 中可以直接看到,有 PICO-8 的朋友也可以在 splore 中找到 InfiniteLoop 下载下来编辑。
如果有感兴趣的朋友可以留言,我再单独写一篇日志。
游戏菜单
我们想在游戏开始前有一个开始菜单用来展示游戏玩法。当然可以在 _draw
函数中判断游戏是否开始,未开始则绘制开始菜单,开始就绘制游戏画面。还有一种更好的方法,使用一个全局变量存储绘制的函数,在同状态下对此变量重新赋值,这样就把游戏和开始菜单的绘制代码分到两个函数中,不会都挤在 _draw
中,影响代码的阅读。
function draw_game() end function draw_menu() end function _init() scn.draw = draw_menu end function _update() if btnp(4,0) then if scn.draw == draw_menu then scn.draw = draw_game end end end function _draw() cls(1) scn.draw() end
- Lua 中函数可以赋值给变量
发布游戏
游戏制作好以后使用 run
运行游戏,在想做游戏封面的地方按下 F7进行截图;然后 ESC 到控制台模式,输入:save loop.p8.png
这时候就在 carts 目录里生成了一个 loop.p8.png 图片,这个类似游戏卡的神奇的图片就包含了游戏所有内容。将此图片提交到官网就可以在 splore 中找到你的游戏啦。简单几步就可以将游戏发布分享,供玩家欣赏或者修改。以下是官网游戏提交链接
接下来我会给大家推荐一些 PICO-8 的游戏
pico-8上的游戏都是小而精
indienova PICO-8 在线玩哟。
看起来不错哦,如果能在手机中运行就更好了,可以随时随地做游戏玩了。希望有更多的 pico-8 内容的介绍呢
@千罹:手机能运行啊,播放图片下面有手机版的新窗口链接……
最近由 游戏发现 修改于:2018-07-24 18:52:24我直接贴这里:https://indienova.com/gamedb/play/u/m/p/infinite-loop
@游戏发现:我的意思是 pico-8 出一个手机版,可以在手机上直接编游戏。就是付费版的那个。也是我的一个小愿景。玩的话可以在网页,我是知道的。
@千罹:明白了,不过会比较累吧
谢谢了,给我很大启发呢