love2d第一章游戏的基本元素

本章内容:love2d的游戏循环以及基本的输入、输出。

游戏的基本元素

教程开启之前,笔者假定你对lua已经有了初步了解,因为在教程中,我不会再讲解关于lua的基础使用。所谓初步了解是指,起码了解lua的数据类型,函数,遍历,条件语句。对自定义迭代器,metatable,与c互动方面暂时不做要求。如有可能,希望了解一些初步的oop(面向对象编程)和eop(面向组件编程)的概念。因为我们未来是写程序的,而写脚本,因此对于程序架构需要有一定的了解。")教程开启之前,笔者假定你对lua已经有了初步了解,因为在教程中,我不会再讲解关于lua的基础使用。所谓初步了解是指,起码了解lua的数据类型,函数,遍历,条件语句。对自定义迭代器,metatable,与c互动方面暂时不做要求。如有可能,希望了解一些初步的oop(面向对象编程)和eop(面向组件编程)的概念。因为我们未来是写程序的,而写脚本,因此对于程序架构需要有一定的了解。

好啦,言归正传,下面我们将开启对游戏的另一个视角,从如何玩游戏到如何做游戏。本章的主要目的是让你更深的了解何为游戏,以及用love2d我们有哪些基本的做游戏的工具。

游戏的基本元素

游戏,换个说法可以说是一种有趣的人机互动。
有趣就意味着你的游戏得有“游戏性”,也就是让人觉得“好玩”,或者起码让人坚持玩下去。
人机互动意味着起码你需要有用户输入,而输出对于现在一般电脑来讲,主要是声音和图像的输出。当然手机有摄像头是输入,有震动是输出。
人机互动还意味着输入和输出的反馈关系,好的输入会产生正向的反馈,比如得分的画面和声音,不好的输入会产生负反馈,比如gameover。而游戏的“好玩”主要就是反馈的感觉如何上体现的。本章我们将用love实现一下简单的这种反馈。
本次我们仅仅学一下游戏最基本的元素,更复杂的元素我们在后面再慢慢道来。

love引擎循环

想要实现游戏,就需要了解love引擎是如何运作的。

首先要理解游戏循环,大家要知道,游戏,窗体等图形界面,程序都不是一闪而过,得出了结构程序就自动退出了,而是一直出于一个循环状态,在循环的某个间隙中,我们把程序的控制权交给程序,而其他时间它都处于休眠的状态。而在这个间隙中,我们要完成程序本身的所有工作,比如检测某个按钮是否被按下,画面如何更新等等。
理解到游戏程序是个循环,我们就可以进行下一步了。
作为一个循环,我们必须在循环之前做一些必要的设置。比如布置好一个场景,生成几个按钮等等。
在循环过程中,我们需要根据用户输入以及一些预先设定好的方式对场景进行变更。
既然场景变更了,那么它的画面表现也要随之变更。
至于如何设置,如何变更,如何绘图,love提供了几个回调函数。所谓回调函数的意义是,平时它不工作,直到特定的条件下,由条件的检测者来触发这些函数。另外,在检测者触发函数时,会传入一些参数。这些参数根据检测者的不同而不同。一句话,所有回调函数是不应该被用户(你们)主动调用的,而是系统(love引擎)来调用他们,而调用的时机也是系统控制的。
love.load函数,是在循环开始前的必要设置时触发。
love.update函数,是在循环时,每当程序拥有控制权时触发。
(程序合适拥有控制权?程序更新的快慢也叫帧率,也就是每秒更新的频率,对于游戏玩家来讲一点都不陌生,简称fps,而每帧之间的时间间隔叫帧时间,简称dt。
提高帧率有助于让画面流畅,降低帧率可以降低cpu或gpu的使用率从而节能。如果开启垂直同步,则默认60帧每秒。垂直同步相关信息请咨询百度。
love.draw函数,是在循环时,游戏逻辑更新后,画面更新时触发。
进阶:对于老手来讲,请自行参阅wiki中的love.run,它是love运行时的一个回调,它控制着love的游戏循环。你可以更深的了解love是如何更新引擎的,同时也可以自行修改,使之更符合自身需求。
同时,love还有其他的回调,即在其他条件下可以触发的回调。比如下面将介绍的键盘按下,鼠标按下等。(其他回调可以参阅wiki中回调部分)
换一句话将,做游戏就是要把对应的功能放到对应回调中就可以了。

love引擎的输入和输出

输入

输入就是让电脑知道你想做什么,人物走停跑跳?车子拐弯?开枪?都需要电脑来读取你的输入。当然,输入也不局限于键盘和鼠标,他们是最基本的输入工具,还有比如游戏杆,触摸屏,重力感应器(手机,还有现在常见的vr设备),红外感应器(比如红外枪),还有主机相关的一堆杂七杂八的设备。下面介绍love2d提供了那些api(编程接口)来帮助用户判断这些输入。

键盘按下

love.keyreleased()  --回调
键盘按住

love.keyboard.isDown() --状态函数
鼠标按下

love.mousereleased() --回调
鼠标按住
love.mouse.isDown() --状态函数

我们发现,有些函数是回调函数,有些函数是状态函数,这里说明一下。回调函数,上面已经介绍了,是在某个状态下就立即触发的,它并不依赖游戏循环。也就是说即使在两帧的间隙中,有按下这个动作,控制者(love内部的事件系统)就会立即call这个函数,从而让你有机会执行到函数内部的内容。而状态函数是一个查询键盘状态的函数,可以任意场合被用户主动调用。
任意键(比如鼠标,键盘)有2种状态和2个回调。状态就是要么一个键是被按下的,要么这个键不被按下。而事件有按下事件和弹起事件,这两个事件会触发相应的回调函数pressed和released。
进阶:一般而言,一个按键处于按下状态时,系统内部会有一个变量来标记你的某个按键处于按下状态。当这个按键不再处于按下状态时,我们检测这个键是否被标记为按下,然后就会触发这个键的弹起事件。有时候,我们不想使用love的按键回调,可以手动的去模拟这么个过程。


local lctrlDown = false

function love.update()

    if love.keyboard.isDown("lctrl") and not lctrlDown then

        lctrlDown = true

        -- lctrl pressed event

    elseif lctrlDown then

        lctrlDown = false

        -- lctrl released event

    end

    if lctrlDown then

        -- hold lctrl

    end

end

输出

输出就是让用户(玩家)能够体验到游戏输入的反馈结果。实际方式也有很多,最常见的就是画面反馈了,然后还有声音,震动,以及其他设备对你感官的刺激(污)。下面介绍的是最基本的画面绘图方式。

设置颜色

设置颜色换一个说法是对某个绘制对象进行染色。如果不做任何设置为白色。颜色有4个参数 r g b a,相关内容请咨询百度。
进阶:alpha值是如何影响各个绘制对象的需要参阅wiki的blendMode部分。类似于ps的图层混合方式。

矢量绘图:


love.graphics.points() --画点

love.graphics.line() --画线

love.graphics.rectangle() --画矩形(圆角)

love.graphics.circle() --画圆

love.graphics.polygon() --画多边形

love.graphics.arc() --画弧形

love.graphics.ellipse() --画椭圆

矢量绘图或者我更喜欢角顶点绘图,是最简单的绘图,在你学习时会经常使用到,或者某些特殊风格的游戏也会直接用到。他们的特点就是你给出一个图形的顶点,然后告诉引擎要填充还是要画线,就可以画出图像了。当然,对于点和线没有填充的意义。一个额外的知识就是,实际上我们画圆(椭圆)并不是圆,而是一些正多边形,边数越多,越圆。所以才会有画圆的最后一个参数。当然大多数时候留空默认就行了。
进阶:聪明的你一定知道如何画一个outlined描边多边形了。描边图形往往更加生动好看些,因为人观察东西主要是观察轮廓。


love.graphics.circle("line",300,300,100)

创建图像对象以及绘制图像

love.graphics.newImage()

love.graphics.draw()

这里要说明的是,对于任何游戏引擎,我们知道的图片,实际上仅仅是数据,都不可以直接用来直接绘图的。而是有一个导入过程。而把图片的数据转化为可以被绘制的对象(在wiki里的那些drawable object)需要使用newImage函数。把他们呈现在画面上用draw函数。我们有时我们听说要减少drawcall提高效率就是指这些(也包括矢量绘图)。
关于draw的参数自己查阅wiki。
love的绘制使用的是屏幕坐标系,也是就是屏幕左上角作为坐标原点,y轴正方向向下,x轴正方向向右。也许初学者对此很不适应,只能慢慢调整了。另外所有图片的锚点默认的也是左上角。(关于锚点是什么意思,可以参考ps的旋转锚点,word图片编辑时也有锚点。)

设置字体以及绘制文字


love.graphics.newFont()

love.graphics.print()

love.graphics.setFont()

与图片制作图片对象一样,绘制文字首先要加载字体,同时规定字体大小。然后要设置这个字体为当前字体才能使用。

在具体学习之前,我十分希望你们自己先wiki一下上面所介绍的api。自己看看这些函数都需要填哪些参数,绘图对象有哪些方法等等,虽然我们可能并不马上用得到。

love编程时间

ok,那么我们开始学习制作第一个love案例,让我们来试试。

设计阶段

在任何编程操作之前,我们都要先进行设计,设计的过程可以在电脑上,纸上或者其他设计工具。在设计阶段,我们要弄清楚,我们最终的作品是什么样的,拥有那些元素,他们的输入和输出方法有哪些,基本的游戏循环涉及哪些元素,文件架构如何,需要引用那些外部资源及使用哪些库文件等等一系列问题。可能目前看起来上面的东西有点复杂,好在我们最开始时不用涉及太多问题,只需要弄清我们将要编程的对象是什么,它有哪些动作,有什么样的画面表现即可。

好的,我们开始设计阶段,首先罗列一个清单:

  1. 我们的游戏对象是一个有一些线条装饰,并且附带一个图片的圆,我们命名为test。
  2. 键盘按住ws时,这个圆每帧横向移动1个像素位置(像素是什么?自己百度吧。)
  3. 键盘按下ad时,这个圆每次纵向移动100个像素。
  4. 鼠标任意键点击,随机重选位置。
  5. test需要画love-ball图片,需要一个线框圆圈以及一个第透明度的方块还要把文字写在下面。
    好啦,以上就是我们的设计内容,是不是很简单。上面几步我们基本实现了互动,只是还没有涉及“游戏性”的部分,作为一个简单的例子,已经足够啦。

实现阶段

首先,我们新建一个main.lua文件,然后敲入我们的可以用到的回调。

然后,根据设计,我们建立一个test对象,是的,lua table是我们最理想的对象形式,它融合了数组、构造、稀疏表等功能,他可以包容数字,字符,表以及函数等等。总之用它没错^^
我们给test一些属性,比如他的位置x,y,它的图像对象image,它的文字对象“hello lovers!”我们后面图像和位置都需要更新他们。
我们知道,设置部分要写在love.load里面,于是有一下代码

function love.load()

    test = {

        x = 400, --屏幕中央

        y = 300,

        image = love.graphics.newImage("images/love-ball.png"),

        text = "hello lovers!"

    }

end

关于lua的书写方面,我建议大家养成比较好的书写习惯,尽量避免用中文作为变量名(虽然这并不产生任何错误),比如合理的变量名,合理的空格和折行。总之好看就好。代码如果很乱的话,自己看也看不清,更何况别人了。

实现键盘按住,在update中使用isDown函数,也就是每帧看看键盘是否按住,如果按住则进行纵向移动。
实现键盘按下,则不属于更新部分,因为无论是否有Update更新,这个回调都单独响应。于是在love.keyrelease下输入平移的判断。
实现鼠标点击随机移动,跟键盘按键类似。这里使用了随机函数,love.math.random,请自行查阅wiki。
插一句,love的窗体默认状态下是800*600垂直同步。你可以通过love.conf来初始设置。以及love.graphics.setMode来动态调整。
update的回调函数有个参数叫dt,我们之前说的帧时间,我们暂时不用。可以使用love.timer.getDelta来获取。


function love.update(dt)

    if love.keyboard.isDown("w") then

        test.y = test.y - 1 --上

    elseif love.keyboard.isDown("s") then

        test.y = test.y + 1 --下

    end

end

function love.keyreleased(key)

    if key == "a" then

        test.x = test.x - 100 --左

    elseif key == "d" then

        test.x = test.x + 100 --右

    end

end

function love.mousereleased(key)

    test.x = love.math.random(1,800) -- 1到800间随机取

    test.y = love.math.random(1,600)

end

接下来,我们让test呈现在我们屏幕上,绘制可能遇到的参数包括,x,y位置,r旋转,sx,sy按坐标轴缩放,ox,oy锚点位置,kx,ky平行畸变(暂时不要管他)
进阶:如果知道4*4Matrix的童鞋应该能够理解,这些东西实际上只是矩阵的简单化。当然你也可以用矩阵方式重新做这些。
注意,锚点位置也会影响缩放中心和旋转中心,所以仅仅使用手动平移的画会有问题的。

function love.draw()

    love.graphics.setColor(255, 255, 255, 255)

    love.graphics.draw(test.image,test.x,test.y,0,1,1,32,32) --图片的宽高是64*64,如何查?请查阅image对象

    --love.graphics.draw(test.image,test.x - 32,test.y - 32) --同样的效果,但是不方便未来的拓展。

    love.graphics.setColor(255, 0, 0, 255)

    love.graphics.circle("line", test.x, test.y, 100) --圆的中心当然是圆心了

    love.graphics.setColor(0, 255, 255, 50)

    love.graphics.rectangle("fill", test.x - 100, test.y - 100, 200, 200) --矩形的起点是他的左上角

    love.graphics.setColor(255, 255, 255, 255)

    love.graphics.print(test.text, test.x-100, test.y+100) --字的锚点也是左上角

end

如果会用ps或则其他绘图软件的人,其实应该很容易理解,可以想象每个drawcall相当于ps中的一个图层,可以对图层进行颜色设置。而后面的图层会覆盖前面的图层,图层间也有相应的混合模式,矢量绘图实际上就是绘制路径,而绘制图片对象就相当于一个普通的像素图层。只不过我们用代码来控制他们绘制的位置。而且是实时控制的。是不是这样就会很容易理解游戏绘图的本质了?
当然,要注意的是,love在每次绘图时,都会用背景颜色重新填充整个屏幕,所以需要每一帧都重画。至于画一次静态图,就不需要每次都重绘,就需要用到canvas,这里先不做讲解,有兴趣的可以自己试试。

上面的代码就包含了,做一个游戏所需的互动内容。是不是很简单? 我们现在实现了第一步。接下来的一课,我们希望把游戏互动变得更加复杂和更有意思。就要在update里动手脚了。

作业

任意替换本课的图片内容,文字,颜色,矢量绘图,互动方式等
用矢量绘图绘制一个安卓机器人或者其他的东西。
在PS中绘制一些东西,在love里重新绘制,理解love绘图。
试试其他移动游戏对象的方式。我们下节课会具体讲解。

本课代码


function love.load()

    test = {

        x = 400,

        y = 300,

        image = love.graphics.newImage("images/love-ball.png"),

        text = "hello lovers!"

    }

end

function love.update(dt)

    if love.keyboard.isDown("w") then

        test.y = test.y - 1

    elseif love.keyboard.isDown("s") then

        test.y = test.y + 1

    end

end

function love.draw()

    love.graphics.setColor(255, 255, 255, 255)

    love.graphics.draw(test.image,test.x,test.y,0,1,1,32,32)

    love.graphics.setColor(255, 0, 0, 255)

    love.graphics.circle("line", test.x, test.y, 100)

    love.graphics.setColor(0, 255, 255, 50)

    love.graphics.rectangle("fill", test.x - 100, test.y - 100, 200, 200)

    love.graphics.setColor(255, 255, 255, 255)

    love.graphics.print(test.text, test.x-100, test.y+100)

end

function love.keyreleased(key)

    if key == "a" then

        test.x = test.x - 100

    elseif key == "d" then

        test.x = test.x + 100

    end

end

function love.mousereleased(key)

    test.x = love.math.random(1,800)

    test.y = love.math.random(1,600)

end


本文转载Alexar博客仅为学习交流

你可能感兴趣的:(love2d第一章游戏的基本元素)