raspberry pi
Raspberry Pi以向孩子介绍开源软件和编程而闻名。 Pi是负担得起的,实用的专业级计算入门,伪装成可破解的乐趣。 Mitch Resnick's Scratch (最幸运的是,它使 Pilot 2改用非开放式Adobe Air时, 由Pi Foundation分叉)是为使幼儿开始编程所做的最大努力,但是一个不可避免的问题是,有人应该毕业于在他们的拖放编程已不复存在之后。
在像Scratch这样的拖放式介绍之后,有很多候选人可以进行下一级别的编程。 有出色的PyGame ,还有一个名为Processing的Java子集,功能强大的Godot引擎等。 诀窍是找到一个框架,该框架足够容易,可以轻松地实现从拖放的即时满足过渡,但又足够复杂,可以准确地表示专业程序员一整天的实际工作。
一个特别强大的游戏引擎称为LÖVE 。 LÖVE使用脚本语言Lua ,它没有像Python那样引起人们的广泛关注,但是在现代视频游戏行业中被大量嵌入 (无论是字面意义上还是图形上)。 几乎所有主要游戏工作室都将Lua列为必备技能,它是专有的Unity和Unreal游戏引擎中的一种选择,并且它通常会在现实世界中各种意外的地方弹出。
简而言之,Lua是一种值得学习的语言,特别是如果您打算进行游戏开发。 就LÖVE引擎而言,就功能而言,它与PyGame框架一样好,并且因为它没有IDE,所以它非常轻巧,并且在某种程度上来说,它不像Godot这样简单。
更好的是,LÖVE可以在Pi上本地运行,但是它的项目可以打开并在Lua可以运行的任何平台上运行。 其中包括Linux,Windows和Mac,还包括Android甚至封闭的iOS。 换句话说,LÖVE也不是开始进行移动开发的不错平台。
Rescue先生,itch.io上的开源游戏
现在,我已经在LÖVE上卖给了您,这是从Pi上的Scratch迈出的下一步,让我们深入研究一下它是如何工作的。
安装LÖVE
像往常一样,在Raspberry Pi上安装LÖVE只是一个简单的命令:
$ sudo apt install love2d
$
sudo dnf
install love2d
无论哪种方式,程序包管理系统都会提取Lua,SDL和LÖVE需要运行的其他依赖项。
你好,世界!
LÖVE引擎没什么可看的。 它实际上是一个框架,意味着您可以使用任何喜欢的文本编辑器。 首先要尝试的是“ hello world”程序,以确保其启动,并向您介绍Lua语言和LÖVE框架的基本语法。 打开文本编辑器,然后输入以下文本:
cwide =
520
chigh =
333
love.
window .
setTitle
(
' Hello Wörld '
)
love.
window .
setMode
( cwide, chigh
)
function love.
load
(
)
love.
graphics .
setBackgroundColor
(
177 ,
106 ,
248
)
-- font = love.
graphics .
setNewFont
(
"SkirtGirl.ttf" ,
72
)
end
function love.
draw
(
)
love.
graphics .
setColor
(
255 ,
33 ,
78
)
love.
graphics .
print
(
"Hello World" , cwide
/
4 , chigh
/
3.33
)
end
将其另存为main.lua 。
可分发的LÖVE软件包只是一个标准的zip文件,扩展名为.love 。 必须始终命名为main.lua的主文件必须位于zip文件的顶层。 像这样创建它:
$ zip hello.love main.lua
并启动它:
$ love ./hello.love
该代码非常容易理解。 cwide和chigh变量是全局变量,可用于整个脚本,它们设置游戏世界的宽度和高度。 在第一个代码块(称为函数 )中,设置了背景色,在第二个函数中,“ hello world”被打印到了屏幕上。
前两个破折号( - )开头的行是注释。 在.love包中包含.ttf并取消注释setNewFont行,将以该字体显示文本。 当然,您不必使用Skirt Girl字体; 只需从FontLibrary.org获取字体并相应地调整代码即可。
$
zip hello.love main.lua SkirtGirl.ttf
并启动它:
$ love .
/ hello.love
基础
Scratch与专业编程语言之间的最大区别是,使用Scratch,您可以学习一些基本原理,然后随机进行探索,直到发现所有其他功能为止。
使用Lua等较低级别的编程语言,您首先学习的东西不是可以复制和粘贴以产生可玩游戏的东西。 您学习了如何使用该语言,然后查找了一些功能,这些功能可以完成您希望游戏发挥作用的动作。 除非您了解语言的工作原理,否则仍然很难进入游戏世界。
幸运的是,您可以在开始游戏时学习该语言。 在此过程中,您会选择一些技巧,以后可以重用这些技巧,以使游戏按照您希望的方式工作。
首先,您需要了解核心LÖVE引擎的三个主要功能:
- 函数love.load()是启动LÖVE游戏时触发的函数(在其他语言中称为init或void setup()函数)。 它用于为您的游戏世界奠定基础。 函数love.load()仅执行一次,因此它仅包含在整个游戏中持续存在的代码。 它或多或少相当于“ 单击绿色标志时”块和Scratch中的“ 阶段”脚本区域。
- 函数love.update(dt)是在游戏过程中不断更新的函数。 玩游戏时,会刷新love.update(dt)函数中的所有内容。 如果您玩过任何游戏,无论是在《弗雷迪的五夜》还是《天际》还是其他游戏中,并且您已经监视了帧速率(fps),那么随着帧跳动而发生的所有事情都将处于更新循环中(实际上不是,因为游戏不是用LÖVE制作的,而是类似更新功能的东西。
- 函数love.draw()是使引擎实例化您在游戏的love.load()函数中创建的图形组件的函数。 您可以加载精灵或创建山峰,但如果不绘制它,则它永远不会出现在游戏中。
这三个功能是您在LÖVE中创建的任何元素的基础。 您也可以创建自己的新功能。 首先,让我们探索LÖVE框架。
精灵
“您好,世界!”的基础知识 是一个很好的起点。 相同的基本三个功能仍充当应用程序的基本框架。 但是,与在屏幕上呈现简单文本不同,这次我们使用freesvg.org中的图形创建一个sprite。
git存储库中提供了此示例的代码示例和资产。 如果要继续,请自己克隆它:
$
git clone https:
// notabug.org
/ seth
/ lessons_love2d.git
LÖVE中的大多数对象都存储在数组中。 数组有点像Scratch中的列表。 它是一堆放置在普通容器中的特征。 通常,在LÖVE中创建子画面时,会创建一个数组来保存有关子画面的属性,然后在love.load中列出希望对象具有的属性。
在love.draw函数中,子画面被绘制到屏幕上。
fish =
{
}
cwide =
520
chigh =
333
love.
window .
setMode
( cwide, chigh
)
love.
window .
setTitle
(
' Collide '
)
function love.
load
(
)
fish.
x =
0
fish.
y =
0
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
end
function love.
update
( dt
)
-- this is a comment
end
function love.
draw
(
)
love.
graphics .
draw
( fish.
img , fish.
x , fish.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
end
鱼的天敌是南极最恶的生物之一:企鹅。 以创建鱼精灵的方式创建企鹅精灵,并添加一个player.speed ,这是一旦我们开始设置玩家控件,企鹅将移动多少像素:
fish =
{
}
player=
{
}
cwide =
520
chigh =
333
love.
window .
setMode
( cwide, chigh
)
love.
window .
setTitle
(
' Collide '
)
function love.
load
(
)
fish.
x =
0
fish.
y =
0
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
player.
x =
100
player.
y =
100
player.
img = love.
graphics .
newImage
(
'images/tux.png'
)
player.
speed =
10
end
function love.
update
( dt
)
-- this is a comment
end
function love.
draw
(
)
love.
graphics .
draw
( fish.
img , fish.
x , fish.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
love.
graphics .
draw
( player.
img ,player.
x ,player.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
end
为了使内容整洁,请将png文件放在images目录中。 要测试到目前为止的游戏,请压缩main.lua和png文件:
$
zip game.zip main.lua
-r images
$
mv game.zip game.love
$ love .
/ game.love
我们目前的角色
运动
有多种方法可以在LÖVE中实现移动,包括鼠标,操纵杆和键盘功能。 我们已经为玩家的x和y位置以及玩家移动了多少像素建立了变量,因此使用if / then语句检测按键,然后使用简单的数学方法重新定义保持玩家精灵位置的变量。 :
player =
{
}
fish =
{
}
cwide =
520
chigh =
333
love.
window .
setMode
( cwide, chigh
)
love.
window .
setTitle
(
' Collide '
)
function love.
load
(
)
fish.
x =
0
fish.
y =
0
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
player.
x =
100
player.
y =
100
player.
img = love.
graphics .
newImage
(
'images/tux.png'
)
player.
speed =
10
end
function love.
update
( dt
)
if love.
keyboard .
isDown
(
"right"
)
then
player.
x = player.
x
+ player.
speed
elseif love.
keyboard .
isDown
(
"left"
)
then
player.
x = player.
x
- player.
speed
elseif love.
keyboard .
isDown
(
"up"
)
then
player.
y = player.
y
- player.
speed
elseif love.
keyboard .
isDown
(
"down"
)
then
player.
y = player.
y
+ player.
speed
end
end
function love.
draw
(
)
love.
graphics .
draw
( player.
img ,player.
x ,player.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
love.
graphics .
draw
( fish.
img , fish.
x , fish.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
end
测试代码以确认运动是否按预期进行。 请记住在测试之前重新压缩所有文件,以免意外测试游戏的旧版本。
为了使运动更有意义,我们可以创建函数来根据按下的键来更改精灵面对的方向。 这等效于以下几种Scratch代码块:
在Scratch中寻找和移动
使用ImageMagick生成播放器精灵的翻转版本:
$ convert tux.png
-flop tuxleft.png
然后编写一个函数以换出图像。 您需要两个功能:一个用于交换图像,另一个用于将其还原为原始图像:
function rotate_left
(
)
player.
img = love.
graphics .
newImage
(
'images/tuxleft.png'
)
end
function rotate_right
(
)
player.
img = love.
graphics .
newImage
(
'images/tux.png'
)
end
在适当的地方使用这些功能:
function love.
update
( dt
)
if love.
keyboard .
isDown
(
"right"
)
then
player.
x = player.
x
+ player.
speed
rotate_right
(
)
elseif love.
keyboard .
isDown
(
"left"
)
then
player.
x = player.
x
- player.
speed
rotate_left
(
)
elseif love.
keyboard .
isDown
(
"up"
)
then
player.
y = player.
y
- player.
speed
elseif love.
keyboard .
isDown
(
"down"
)
then
player.
y = player.
y
+ player.
speed
end
end
自动运动
使用与精灵x值相同的约定以及一些数学运算,可以使鱼自动在屏幕上从另一边移到另一边。 这相当于在Scratch中执行此操作:
Scratch中的自动机芯
如果没有,就用LÖVE 反弹 ,而是通过检查鱼的x值是否到达屏幕的最左端( 0像素)或最右边(与画布的宽度相同)( cwide变量),则可以确定该精灵是否偏离边缘。
如果鱼在最左边,则它已到达屏幕的边缘,因此您增加了鱼的位置,迫使其向右移动。 如果鱼在最右边,则减小鱼的位置,使其向左移动。
function automove
( obj,x,y,ox,oy
)
if obj.
x == cwide
then
local edgeright =
0
elseif obj.
x ==
0
then
local edgeright =
1
end
if edgeright ==
1
then
obj.
x = obj.
x
+ x
else
obj.
x = obj.
x
- x
end
end
在这种情况下,有一系列事件很重要。 在第一个if块中,检查x的值并分配一个临时(局部)变量来指示下一步需要去哪条鱼。 然后,在第二个单独的if块中移动鱼。 为了获得加分,请尝试在if语句中全部完成,然后查看是否可以理解为什么会失败。 要获得更多的奖励积分,请查看您是否可以找出另一种移动鱼的方法。
要实现鱼的移动功能,请在love.update循环底部调用该功能:
automove(fish,1,0,fish.img:getWidth(),fish.img:getHeight() )
如果您对该脚本进行测试,您会注意到,当鱼碰到右边缘时,它会一直离开屏幕。 之所以这样做,是因为Sprite的x值基于其左上像素。 我将把它留作练习,让您确定在检查鱼的位置时应从cwide中减去什么变量。
碰撞检测
电子游戏都是关于碰撞的。 当事情相互碰撞时,无论这些事情是不幸的英雄进入了熔岩坑,还是一个坏人被法术力炸弹炸开,都应该发生。
在检测到碰撞之前,让我们决定发生碰撞时想要发生的事情。 因为您已经知道如何更改子画面的外观,所以我们将创建两个功能:一个将企鹅捕获到的鱼变成残骸中的鱼,另一个将在企鹅不在时将其恢复生命。不在。
function falive
(
)
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
end
function fdead
(
)
fish.
img = love.
graphics .
newImage
(
'images/fishbones.png'
)
end
设置好这些功能后,就该计算碰撞了。
在Scratch中,有代码块可以检查两个Sprite是否接触。
刮擦碰撞
原则上,相同的概念适用于LÖVE。 有多种检测碰撞的方法,包括HC和love.physics之类的外部库,但两者之间的一个很好的折衷方案是自定义函数,用于检测精灵边界的重叠。
function CheckCollision
( x1,y1,w1,h1, x2,y2,w2,h2
)
return x1
< x2
+ w2
and
x2
< x1
+ w1
and
y1
< y2
+ h2
and
y2
< y1
+ h1
end
数学很复杂,但是如果您花一点时间考虑一下,这很有意义。 目的是检测两个图像是否重叠。 如果图像重叠,则可以说它们发生了碰撞。 这是两个不 *重叠的框的示例,它们的y值示例使事情简单:
两盒
从函数中取出一行并处理数字:
y2
< y1
+ h1
110
<
0
+
100
显然,这是一条错误的语句,因此该函数必须返回false 。 换句话说,盒子没有碰撞。
现在来看具有两个重叠框的相同逻辑:
y2
< y1
+ h1
50
<
0
+
100
这显然是正确的。 假设函数中的所有语句也都是true(如果我不愿意添加x值,它们也会是true),那么函数将返回true 。
要利用冲突检查,请使用if语句对其进行评估。 现在您知道,如果CheckCollision函数返回true ,则发生冲突。 CheckCollision函数是通用编写的,因此在调用它时,您需要向其提供适当的值,以便它知道哪个对象是哪个对象。
大多数值是直观的。 您需要使用LÖVE才能使用要检查碰撞的每个对象的x和y位置以及对象的大小。 在这种情况下,唯一的特殊值是根据碰撞状态被换出的值。 对于那些,硬编码死鱼而不是活鱼的大小,否则在碰撞检测过程中碰撞状态将发生改变。 实际上,如果您想看到它的毛刺,可以先用错误的方式做:
if CheckCollision
( fish.
x ,fish.
y ,fish.
img :getWidth
(
) ,fish.
img :getHeight
(
) , player.
x ,player.
y ,player.
img :getWidth
(
) ,player.
img :getHeight
(
)
)
then
fdead
(
)
else
falive
(
)
end
正确的方法是获取较小的Sprite图片的大小。 您可以使用ImageMagick做到这一点:
$ identify images
/ fishbones.png
images
/ fishbones.png PNG 150x61
[ ...
]
然后用适当的尺寸对“热点”进行硬编码:
if CheckCollision
( fish.
x ,fish.
y ,
150 ,
61 , player.
x ,player.
y ,player.
img :getWidth
(
) ,player.
img :getHeight
(
)
)
then
fdead
(
)
else
falive
(
)
end
硬编码碰撞检测区域的副作用是,当企鹅接触鱼的边缘时,鱼不会被吞噬。 为了更精确地检测碰撞,请浏览HC库或love.physics 。
最终代码
它不算什么游戏,但它展示了视频游戏的重要元素:
player =
{
}
fish =
{
}
cwide =
520
chigh =
333
love.
window .
setTitle
(
' Hello Game Wörld '
)
love.
window .
setMode
( cwide, chigh
)
function love.
load
(
)
fish.
x =
0
fish.
y =
0
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
player.
x =
150
player.
y =
150
player.
img = love.
graphics .
newImage
(
'images/tux.png'
)
player.
speed =
10
end
function love.
update
( dt
)
if love.
keyboard .
isDown
(
"right"
)
then
player.
x = player.
x
+ player.
speed
elseif love.
keyboard .
isDown
(
"left"
)
then
player.
x = player.
x
- player.
speed
elseif love.
keyboard .
isDown
(
"up"
)
then
player.
y = player.
y
- player.
speed
elseif love.
keyboard .
isDown
(
"down"
)
then
player.
y = player.
y
+ player.
speed
end
if CheckCollision
( fish.
x ,fish.
y ,
151 ,
61 , player.
x ,player.
y ,player.
img :getWidth
(
) ,player.
img :getHeight
(
)
)
then
fdead
(
)
else
falive
(
)
end
automove
( fish,
1 ,
0 ,fish.
wide ,fish.
high
)
end
function love.
draw
(
)
love.
graphics .
draw
( player.
img ,player.
x ,player.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
love.
graphics .
draw
( fish.
img , fish.
x , fish.
y ,
0 ,
1 ,
1 ,
0 ,
0
)
end
function automove
( obj,x,y,ox,oy
)
if obj.
x == cwide
- fish.
img :getWidth
(
)
then
edgeright =
0
elseif obj.
x ==
0
then
edgeright =
1
end
if edgeright ==
1
then
obj.
x = obj.
x
+ x
else
obj.
x = obj.
x
- x
end
end
function CheckCollision
( x1,y1,w1,h1, x2,y2,w2,h2
)
return x1
< x2
+ w2
and
x2
< x1
+ w1
and
y1
< y2
+ h2
and
y2
< y1
+ h1
end
function rotate_left
(
)
player.
img = love.
graphics .
newImage
(
'images/tuxleft.png'
)
end
function rotate_right
(
)
player.
img = love.
graphics .
newImage
(
'images/tux.png'
)
end
function falive
(
)
fish.
img = love.
graphics .
newImage
(
'images/fish.png'
)
end
function fdead
(
)
fish.
img = love.
graphics .
newImage
(
'images/fishbones.png'
)
end
在这里,您可以使用学到的原理来创作更多精彩的作品。 碰撞是视频游戏中大多数交互的基础,无论是触发与NPC的对话,管理战斗,捡起物品,引诱陷阱,还是几乎其他所有事情,因此,如果您掌握了这些,其余就是重复和肘部润滑。
因此,去制作一个视频游戏吧! 与朋友分享,在移动设备上播放,并一如既往地不断升级。
翻译自: https://opensource.com/article/17/4/how-program-games-raspberry-pi
raspberry pi