PICO-8

创建于:2017-11-26

创建人: Humble Ray

5 信息 18 成员
Fantasy Console 的代表 PICO-8 小巧精美多变
pico-8 精彩代码解析 4
83872309 2019-11-02

Image title

作者: Luca Harris @lucatron_

来源:twitter


整理了一下作者的代码,共计15行:

for i=0,11 do
 pal(i,({135,10,9,137,8,136,2,130,128})[i+1],1)
end
function _draw()
 srand(3)
 rectfill(0,0,127,40,0)
 for y=52,170,2 do
  k=y/19-2
  x=y*44%138-4
  h=40+rnd(20)
  for i=-20,20 do
   line(x+i*h/70,y,x+sin(t()/4+x/500+y/300)/7*h,y-h,i>0 and k+2 or k)
  end
 end
end

1-3行, 将调色板的颜色映射,这里用到了第二块调色板的颜色,更好的表示渐变。关于第二块调色板的内容,在解析 2里有详细说明。

5行, 初始随机数种子,在计算树木高度的时候详细解释为什么每次绘制要初始随机数

6行, 画背景,我们用cls()代替手动绘制背景,可以更好的测试

7行, 循环得到y,y值是树的y轴位置,从52-170,每两个树之间相差2

8行, k用来将树按照y值的顺序,映射到调色板的色值,实现近景到远景的颜色渐变

9行, 计算树的x坐标,根据y轴坐标的倍数与138取余,就把树木限制在0-138的范围内一排排的码好。

10行, 计算树木的高度h,这里每棵树的高度是随机的,但是每帧绘制的时候同一棵树的高度是不变的,也就是每一帧绘制的时候rnd(20)会得到同一个随机值,这就得益于在第5行,绘制开始的时候重新初始化随机数种子,使得每帧获得随机数是一致的。

11-13行,绘制一棵树。pico8没有直接绘制三角形的函数,这里用40根线段拼成了一个三角形。我们先看下简化的代码,line(x+i,y,x,y-h,i>0 and k+2 or k) 。i值(-20 -> 20)是三角形底边的x轴偏移量,从线段 起点(x-20,y) 终点 (x,y-h) 到  起点 (x+20,y) 终点 (x,y-h)] 共计画了40跟线段组成了三角形,三角形的底边宽40。简化的效果图如下。

Image title

line(x+i*h/70,y,x+sin(t()/4+x/500+y/300)/7*h,y-h,i>0 and k+2 or k) 原代码计算起点时将偏移量i乘以树木的高度h/70用来表示,矮的树木拥有更窄的底边。计算终点时,树尖的x坐标根据时间,取正弦值,用来模拟来回摆动的效果,且越矮的树木摆动幅度越低。最后根据偏移量i来觉得颜色,i<0为树木左半边,i>0为树木右半边。


(转发自:原日志地址
pico-8 精彩代码解析 3
83872309 2019-11-01

Image title


作者: Luca@lucatron_

来源:twitter


整理过后14行代码:

c={10,9,8,2,1,0}
fillp(0xa5a5)
function _draw()
 for i=24576,32767 do
  poke(i,peek(i)/2)
 end
 for z=6,1,-.1 do
  for b=0,7 do
   b=b/8+sin(z/8+t()/4)/30
   k=19-sin((6-z)/20)*40*(.5+sin(t()/2+z/9+b*2)/2)
   circfill(64+cos(b)*k,64+sin(b)*k,z*2.3,c[flr(z)]+c[flr(z+.5)]*16)
  end
 end
end

1-2行,c是触手的颜色,顶部到底部由明止暗。fillp是填充样式,点阵图样填充,有种渐变的效果(在最后详细解释)。

Image title


4-6行,是残影效果,这里没有用cls清空画布,直接将屏幕内存中的数值每帧减半,最终降为0透明,来实现透明效果

Image title


10-11行,两个嵌套循环,外层的z表示,每个触手的层数,内层b表示触手的数量(8个)。单个触手是由从下至上,大小递减,颜色逐渐变亮的圆组成,z表示圆的位置,相当于z轴的坐标,从6-1共有60层。

9行,b/8 将八只触手映射到 0-1,pico8中三角函数的参数取值0-1代表着0-360°。在第11行的circlefill 圆的绘制函数中,圆B的位置 x,y =  64+cos(b)*k,64+sin(b)*k 。 其中原点是屏幕的中心点64,64;角度b为每个触手映射的角度,k为圆至屏幕中心的距离。

Image title


10行 计算每个圆至屏幕中心的距离k,我们先把圆的角度b从中去掉看下结果。k=19-sin((6-z)/20)*40*(.5+sin(t()/2+z/9)/2)

Image title


将圆的z轴坐标z映射到至中心的距离k,通过sin的映射使得,距离k根据z呈现正弦波的形状。

最后把角度b加回去,k=19-sin((6-z)/20)*40*(.5+sin(t()/2+z/9+b*2)/2) 使得不同的触手在波形上拥有不同的位置。

Image title


第11行 circlefill 画圆,其中圆的半径 根据z轴距离变化,z*2.3。圆的颜色c[flr(z)]+c[flr(z+.5)]*16 是两个颜色相加,利用高低位的不同颜色,再配合第2行的fillp实现两种颜色的填充图样。


最后补充下fillp的用法。pico8中用一个4*4的图来填充 circ() circfill() rect() rectfill() pset() line()等函数画的图形。

Image title

16个像素点用上图表格里的数字代表,如果想让某个点不着色就将点对应的数值累加,将最终得到的值传给fillp,比如只让对角线

32768,1024,32,1没有颜色,fillp(32768+1024+32+1)

Image title


也可以7+8*16 指定第二个颜色。



(转发自:原日志地址
pico-8 精彩代码解析 1
83872309 2019-10-21


pico-8 的社区是一个满是热情与分享精神的社区,无数玩家与开发者贡献着代码。本系列日志将选取其中比较精彩的代码片段进行解析,学习其中的算法思路。


效果截图:


Image title

作者:2DArray 

来源:twiiter

一个有趣的植物沿着物体生长的gfx,作者在twitter上贴了代码(由于404的原因我就不发原文连接了)

Image title


代码比较简单,我格式化了一下,只有19行

cls()
print("grow")
line(0,5,15,5,11)
memcpy(0,24576,384)
cls()
sspr(0,0,15,6,11,70,105,35)
::_::
x=rnd(128)
c=0
y=rnd(64)+64
u=x+rnd(2)-1
v=y-1
for a=u-1,u+1 do
 for b=v-1,v+1 do
  if(pget(a,b)%6>0)c+=1
 end
end
if(pget(x,y)%6>0 and(pget(u,v)==6 or rnd()<.1) and c<6) pset(u,v,3+c%2*8)
goto _

前6行是在屏幕下半部分中打印“GROW”,后面再解释。先来看看最精彩的循环部分7-19行。

8-12行定义了5个变量:

  • c 用来计数
  • x 在横坐标随机取值,y 在64-128范围内随机取值;很明显这是在下半个屏幕中随机取坐标点,即“GROW”所在区域
  • u 随机取[x-1,x,x+1] 在x值左中右3个值中随机, v =y-1 是y值上方的点

这样算法的意图就比较明显了,在显示范围内随机取点,在该点上方左中右方向上延伸,以模拟植物向上分叉生长的感觉。我们可以把u改成  u=x 使得植物只向上方生长。

Image title


也可以另 v=y+1使像素点向下延伸。


第13-17行的两个嵌套for循环,将(u,v)所在的附近九宫格内的像素点遍历一遍;if(pget(a,b)%6>0)c+=1,取其颜色值(0-15) 与6取余,大于0则c加一。其实就是计算(u.v) 附近的颜色非0,6的像素的数量c,这里背景色是黑色0, "GROW" 是灰色 6。


第18行,根据3个判断将点(u,v)着色;

  • pget(x,y)%6>0  (u,v)下方的点(x,y)不能是背景色0或者物体的颜色6,也就是草不能从背景或者物体上长出,这里只能从底部绿色土地长出。如下图改变了字体的颜色,草就从字体上长出来了。
  • pget(u,v)==6 or rnd()<0.1  点(u,v)的像素如果是字体颜色6,则可以着色,否则只有0.1的概率会着色。这里模拟植物优先在字体表面增长,空白部位只有很少的草在增长。如下图将字体颜色改成粉色14,草就无法在字体上生长。
  • c<6  点(u,v)周围九宫格范围内的草的数量,如果多余6个则不在生长,用于控制草的密度。

Image title

最后根据pset(u,v,3+c%2*8) 密度用两种绿色进行着色。简单明了的实现了植物生长的特效。


最后解释一下1-6行在屏幕上打印"GROW"的代码。2-3行在屏幕的左上角打印grow并在字体下方加上绿色的下划线。第4行 memcpy(0,24576,384) 将屏幕内容所在的内存地址24576后的384 bytes 复制到精灵内存地址0。pico-8中24576开的内存存放的是屏幕中的像素颜色信息,一个bytes(8位)可以存放两个点,低4位和高4位分别储存一个点的颜色信息,这也是pico-8只能有2的4次方,16种颜色的原因。那384=128*6/2 正好是屏幕中打印的grow所在的前6行,将打印的grow拷贝到存放的精灵的地址中,再使用sspr函数将精灵放大显示到屏幕的中心。

这样复杂的操作仅仅是为了打印一个字号比较大的GROW而已,因为自带的print函数不能设置字号。这样做会把pico-8中精灵编辑器中前6行的内容都覆盖掉,所以直接在精灵编辑器中编辑再显示在屏幕中是个更好的选择。这里作者肯定是为了简便才使用这种直接打字的方式。


总结

这种随机遍历每个像素点来实现的特效会消耗大量的cpu,用在游戏中的话还需要做相应的简化。虽然这种奇技淫巧看起来很难用到游戏里,但是这也是可以逐像素操作的pico-8魅力所在。当然在其他游戏引擎中,就可以用像素着色器(shader)更有效率的实现这种算法,使其变得可用。作者使用 u,v做变量名,应该也是shader写法的影响。


Image title



(转发自:原日志地址
pico-8 精彩代码解析 1
83872309 2019-10-21


pico-8 的社区是一个满是热情与分享精神的社区,无数玩家与开发者贡献着代码。本系列日志将选取其中比较精彩的代码片段进行解析,学习其中的算法思路。


效果截图:


Image title

作者:2DArray 

来源:twiiter

一个有趣的植物沿着物体生长的gfx,作者在twitter上贴了代码(由于404的原因我就不发原文连接了)

Image title


代码比较简单,我格式化了一下,只有19行

cls()
print("grow")
line(0,5,15,5,11)
memcpy(0,24576,384)
cls()
sspr(0,0,15,6,11,70,105,35)
::_::
x=rnd(128)
c=0
y=rnd(64)+64
u=x+rnd(2)-1
v=y-1
for a=u-1,u+1 do
 for b=v-1,v+1 do
  if(pget(a,b)%6>0)c+=1
 end
end
if(pget(x,y)%6>0 and(pget(u,v)==6 or rnd()<.1) and c<6) pset(u,v,3+c%2*8)
goto _

前6行是在屏幕下半部分中打印“GROW”,后面再解释。先来看看最精彩的循环部分7-19行。

8-12行定义了5个变量:

  • c 用来计数
  • x 在横坐标随机取值,y 在64-128范围内随机取值;很明显这是在下半个屏幕中随机取坐标点,即“GROW”所在区域
  • u 随机取[x-1,x,x+1] 在x值左中右3个值中随机, v =y-1 是y值上方的点

这样算法的意图就比较明显了,在显示范围内随机取点,在该点上方左中右方向上延伸,以模拟植物向上分叉生长的感觉。我们可以把u改成  u=x 使得植物只向上方生长。

Image title


也可以另 v=y+1使像素点向下延伸。


第13-17行的两个嵌套for循环,将(u,v)所在的附近九宫格内的像素点遍历一遍;if(pget(a,b)%6>0)c+=1,取其颜色值(0-15) 与6取余,大于0则c加一。其实就是计算(u.v) 附近的颜色非0,6的像素的数量c,这里背景色是黑色0, "GROW" 是灰色 6。


第18行,根据3个判断将点(u,v)着色;

  • pget(x,y)%6>0  (u,v)下方的点(x,y)不能是背景色0或者物体的颜色6,也就是草不能从背景或者物体上长出,这里只能从底部绿色土地长出。如下图改变了字体的颜色,草就从字体上长出来了。
  • pget(u,v)==6 or rnd()<0.1  点(u,v)的像素如果是字体颜色6,则可以着色,否则只有0.1的概率会着色。这里模拟植物优先在字体表面增长,空白部位只有很少的草在增长。如下图将字体颜色改成粉色14,草就无法在字体上生长。
  • c<6  点(u,v)周围九宫格范围内的草的数量,如果多余6个则不在生长,用于控制草的密度。

Image title

最后根据pset(u,v,3+c%2*8) 密度用两种绿色进行着色。简单明了的实现了植物生长的特效。


最后解释一下1-6行在屏幕上打印"GROW"的代码。2-3行在屏幕的左上角打印grow并在字体下方加上绿色的下划线。第4行 memcpy(0,24576,384) 将屏幕内容所在的内存地址24576后的384 bytes 复制到精灵内存地址0。pico-8中24576开的内存存放的是屏幕中的像素颜色信息,一个bytes(8位)可以存放两个点,低4位和高4位分别储存一个点的颜色信息,这也是pico-8只能有2的4次方,16种颜色的原因。那384=128*6/2 正好是屏幕中打印的grow所在的前6行,将打印的grow拷贝到存放的精灵的地址中,再使用sspr函数将精灵放大显示到屏幕的中心。

这样复杂的操作仅仅是为了打印一个字号比较大的GROW而已,因为自带的print函数不能设置字号。这样做会把pico-8中精灵编辑器中前6行的内容都覆盖掉,所以直接在精灵编辑器中编辑再显示在屏幕中是个更好的选择。这里作者肯定是为了简便才使用这种直接打字的方式。


总结

这种随机遍历每个像素点来实现的特效会消耗大量的cpu,用在游戏中的话还需要做相应的简化。虽然这种奇技淫巧看起来很难用到游戏里,但是这也是可以逐像素操作的pico-8魅力所在。当然在其他游戏引擎中,就可以用像素着色器(shader)更有效率的实现这种算法,使其变得可用。作者使用 u,v做变量名,应该也是shader写法的影响。


Image title



(转发自:原日志地址
pico-8 精彩代码解析 2
83872309 2019-10-21

Image title

作者:2DArray 

来源:twiiter

神奇的漩涡效果,代码发布在作者的twitter上,整理下只有11行:

p={130,141,2,136,8,137,142,9}
for i=1,8 do
pal(i-1,p[i],1)
end
::_::
x=rnd(128)
y=rnd(128)
d=sqrt((x-64)^2+(y-64)^2)-t()*4
a=atan2(y-64,x-64)*16
pset(x,y,(a+d/4)%8)
goto _

1-4行代码是调色板映射代码,用来实现渐变的颜色。我们知道pico8只有16种颜色,16种颜色很难配合出渐变的过渡效果。这里作者用到pico8隐藏的调色板,变量p中的130,141,136,137,142都是隐藏调色板的色号。这个隐藏的调色板在官方文档里也没有介绍,在论坛翻了了好久才找到,新旧两个调色板的对比,我做了个对比图:

Image title

6-9行定义了4个参数:

  • x,y 在屏幕范围内随机取值
  • d 是取的点(x,y)相对于(64,64)即中心点的距离,后面的t()*4 是为了使距离根据运行时间改变
  • a 是点(x,y)相对于中心点的角度,pico8中atan2的返回值是0-1,对应这0°-360°。后面的*16将a的取值变成了0-16

第10行 根据d 和 a的值将对应颜色填充到x,y。我们可以分开来看下

Image title


pset(x,y,a%8)

如果只用a来计算,会将屏幕按照点的到中间点的角度分割成16份,这个比较好理解,因为a的取值是0-16,相当于把屏幕中的点映射到16个区域。


Image title

pset(x,y,(d/4)%8)

d是点到中心的距离,相同距离的圆形区域被填充同一个颜色。


Image title


pset(x,y,(a+d/4)%8)

当把角度和距离加起来的时候神奇的图像就出现了,而且角度a必须于8的倍数相乘才可以对齐,不知道原理是什么,真的很神奇。

Image title


(转发自:原日志地址

加入 indienova

  • 建立个人/工作室档案
  • 建立开发中的游戏档案
  • 关注个人/工作室动态
  • 寻找合作伙伴共同开发
  • 寻求线上发行
  • 更多服务……
登录/注册