Don't Starve,好脚本,好欢乐
最近玩了shank系列的开发公司新出的游戏饥荒(Don't Starve),容量很小,200MB左右,所以可以归类为小游戏。。但是游戏性却是相当的高,游戏中各物件的交互出奇的丰富和复杂,我相信该游戏90%的创意和亮点就在于这丰富的可交互性中(想想神作辐射系列吧,我大学那会玩辐射2可是玩的的不亦乐乎!)。


这么棒的gameplay制作,我们需要把功劳归到开发组策划和程序的完美配合上。他们为什么可以做得这么好捏?我发现可以说是脚本系统完美应用的结果。在游戏目录data\script下,就是游戏的全部lua脚本,可阅读可修改,有Components,有StateGraph,有Behaviours tree,相当的有可参考性!

首先我发现data\script\prefabs目录下是所有对象(物品,角色)的基本配置(组成动画,掉落物品,component组装,behavior赋予)。比如咱们看二师兄pigman的脚本文件。。

local assets =
{
Asset("ANIM", "anim/ds_pig_basic.zip"),
Asset("ANIM", "anim/ds_pig_actions.zip"),
Asset("ANIM", "anim/ds_pig_attacks.zip"),
Asset("ANIM", "anim/pig_build.zip"),
Asset("ANIM", "anim/pigspotted_build.zip"),
Asset("ANIM", "anim/pig_guard_build.zip"),
Asset("ANIM", "anim/werepig_build.zip"),
Asset("ANIM", "anim/werepig_basic.zip"),
Asset("ANIM", "anim/werepig_actions.zip"),
Asset("SOUND", "sound/pig.fsb"),
} local prefabs =
{
"meat",
"monstermeat",
"poop",
"tophat",
"strawhat",
"pigskin",
} local function SetWerePig(inst)
inst:AddTag("werepig")
inst:RemoveTag("guard")
local brain = require "brains/werepigbrain"
inst:SetBrain(brain)
inst:SetStateGraph("SGwerepig")
inst.AnimState:SetBuild("werepig_build") inst.components.sleeper:SetResistance() inst.components.combat:SetDefaultDamage(TUNING.WEREPIG_DAMAGE)
inst.components.combat:SetAttackPeriod(TUNING.WEREPIG_ATTACK_PERIOD)
inst.components.locomotor.runspeed = TUNING.WEREPIG_RUN_SPEED
inst.components.locomotor.walkspeed = TUNING.WEREPIG_WALK_SPEED inst.components.sleeper:SetSleepTest(WerepigSleepTest)
inst.components.sleeper:SetWakeTest(WerepigWakeTest) inst.components.lootdropper:SetLoot({"meat","meat", "pigskin"})
inst.components.lootdropper.numrandomloot = inst.components.health:SetMaxHealth(TUNING.WEREPIG_HEALTH)
inst.components.combat:SetTarget(nil)
inst.components.combat:SetRetargetFunction(, WerepigRetargetFn)
inst.components.combat:SetKeepTargetFunction(WerepigKeepTargetFn) inst.components.trader:Disable()
inst.components.follower:SetLeader(nil)
inst.components.talker:IgnoreAll()
inst.Label:Enable(false)
inst.Label:SetText("")
end local function common()
local inst = CreateEntity()
local trans = inst.entity:AddTransform()
local anim = inst.entity:AddAnimState()
local sound = inst.entity:AddSoundEmitter()
local shadow = inst.entity:AddDynamicShadow()
shadow:SetSize( 1.5, . )
inst.Transform:SetFourFaced() inst.entity:AddLightWatcher()
inst.entity:AddLabel() inst.Label:SetFontSize()
inst.Label:SetFont(TALKINGFONT)
inst.Label:SetPos(,3.8,)
--inst.Label:SetColour(180/255, 50/255, 50/255)
inst.Label:Enable(false) MakeCharacterPhysics(inst, , .) inst:AddComponent("locomotor") -- locomotor must be constructed before the stategraph
inst.components.locomotor.runspeed = TUNING.PIG_RUN_SPEED --
inst.components.locomotor.walkspeed = TUNING.PIG_WALK_SPEED -- inst:AddTag("character")
inst:AddTag("pig")
inst:AddTag("scarytoprey")
anim:SetBank("pigman")
anim:PlayAnimation("idle_loop")
anim:Hide("hat") ------------------------------------------
inst:AddComponent("eater")
inst.components.eater:SetOmnivore()
inst.components.eater:SetCanEatHorrible()
inst.components.eater.strongstomach = true -- can eat monster meat!
inst.components.eater:SetOnEatFn(OnEat)
------------------------------------------
inst:AddComponent("combat")
inst.components.combat.hiteffectsymbol = "pig_torso" MakeMediumBurnableCharacter(inst, "pig_torso") inst:AddComponent("named")
inst.components.named.possiblenames = STRINGS.PIGNAMES
inst.components.named:PickNewName() ------------------------------------------
inst:AddComponent("werebeast")
inst.components.werebeast:SetOnWereFn(SetWerePig)
inst.components.werebeast:SetTriggerLimit() ------------------------------------------
inst:AddComponent("follower")
inst.components.follower.maxfollowtime = TUNING.PIG_LOYALTY_MAXTIME
------------------------------------------
inst:AddComponent("health") ------------------------------------------ inst:AddComponent("inventory") ------------------------------------------ inst:AddComponent("lootdropper") ------------------------------------------ inst:AddComponent("knownlocations")
inst:AddComponent("talker")
inst.components.talker.ontalk = ontalk ------------------------------------------ inst:AddComponent("trader")
inst.components.trader:SetAcceptTest(ShouldAcceptItem)
inst.components.trader.onaccept = OnGetItemFromPlayer
inst.components.trader.onrefuse = OnRefuseItem ------------------------------------------ inst:AddComponent("sanityaura")
inst.components.sanityaura.aurafn = CalcSanityAura ------------------------------------------ inst:AddComponent("sleeper") ------------------------------------------
MakeMediumFreezableCharacter(inst, "pig_torso") ------------------------------------------ inst:AddComponent("inspectable")
inst.components.inspectable.getstatus = function(inst)
if inst:HasTag("werepig") then
return "WEREPIG"
elseif inst:HasTag("guard") then
return "GUARD"
elseif inst.components.follower.leader ~= nil then
return "FOLLOWER"
end
end
------------------------------------------ inst.OnSave = function(inst, data)
data.build = inst.build
end inst.OnLoad = function(inst, data)
if data then
inst.build = data.build or builds[]
if not inst.components.werebeast:IsInWereState() then
inst.AnimState:SetBuild(inst.build)
end
end
end inst:ListenForEvent("attacked", OnAttacked)
inst:ListenForEvent("newcombattarget", OnNewTarget) return inst
end local function normal()
local inst = common()
inst.build = builds[math.random(#builds)]
inst.AnimState:SetBuild(inst.build)
SetNormalPig(inst)
return inst
end local function guard()
local inst = common()
inst.build = guardbuilds[math.random(#guardbuilds)]
inst.AnimState:SetBuild(inst.build)
SetGuardPig(inst)
return inst
end return Prefab( "common/characters/pigman", normal, assets, prefabs),
Prefab("common/character/pigguard", guard, assets, prefabs)
主要值得注意的有几个地方:
- SetStateGraph()
- SetBrain()
- AddComponent()
我阅读了这么一会儿,这3个东西就是一个游戏对象的重要组成部分。它们的脚本分别位于
data\script\stategraphs,data\script\brains,data\script\components目录下。
StateGraph
看这个名字我猜测这个部分应该是处理状态机的吧。二师兄的状态脚本为SGpig.lua。里面定义了一些状态如:funnyidle,death,frozen等。还可以参考data\script下的stategraph.lua文件。
Brain
几乎每个角色都有相应的这个脚本。这个brain对象就是对该角色behavior tree的一个封装。比如二师兄的pigbrain.lua文件。。我们从最上面的require部分可以得知,二师兄可以有这些behavior: wander, follow, runaway, panic, ChaseAndAttack, findlight等。那么我们至少可以得知,该游戏看来是将behavior tree这部分脚本化了,值得学习哟。
behavior方面的脚本主要就是data\script\behaviourtree.lua文件和data\script\behaviours整个目录了。前者定义了行为树类和它的各种五花八门的功能node,序列节点,条件节点,选择节点,优先级节点,并行节点,decorate节点等。后者是一些定义好的behavior。
Component
基于组件的entity系统。既然逻辑都脚本化了,组件模块肯定也要随之脚本化。一来提供开放接口在逻辑脚本中调用,二来方便策划扩展新的组件。
我们看到,二师兄是由这么些组件搭建成的:eater, combat, health, trader, sleeper等等。所有组件都在data\script\Component目录下,相当多,一共167个文件!想知道二师兄为什么战斗跑位这么风骚吗?战斗逻辑就在combat.lua这个组件里。

从该游戏身上,我们要认识到,一个好游戏不是凭空而来的,它的每个亮点都对应了开发人员背后的思考和汗水。仅仅从捞钱出发,把玩家当SB的中国式特色(极品装备,一刷就爆;我不断的寻找,油腻的师姐在哪里!)的开发套路是不可能做出真正的好游戏的!应用脚本系统,把角色怪物配置,状态逻辑,交互逻辑等XXXX逻辑相关的部分脚本化,我觉得在技术上不是特别特别难的事情(策划要给力。。),只要坚持下去做,一定能带来很多好处,让项目良性发展。
- 配合工具,就像星际2的一站式银河编辑器一样,让策划能进行独立设计和扩展,解救客户端程序于无尽的琐碎小事中。
- 我觉得将逻辑代码脚本化后,使得整个客户端的代码整洁性能大幅度提升,一定不能小瞧破窗户理论带来的危害。。逻辑这个脏乱差的模块统一在脚本里管理起来就好多了。我总感觉自己有很严重的代码洁癖。
- 脚本化后,有了对外开放的API集,提供MOD功能也方便了。
Don't Starve,好脚本,好欢乐的更多相关文章
- Gradle Android最新自动化编译脚本教程
转自:http://blog.csdn.net/changemyself/article/details/39927381 一.前言 Gradle 是以 Groovy 语言为基础,面向Java应用为主 ...
- Gradle Android它自己的编译脚本教程的最新举措(提供demo源代码)
一.前言 Gradle 是以 Groovy 语言为基础,面向Java应用为主.基于DSL(领域特定语言)语法的自己主动化构建工具. 上面这句话我认为写得非常官方,大家仅仅需知道Gradle能够用来an ...
- 源码:自己用Python写的iOS项目自动打包脚本
http://www.cocoachina.com/ios/20160307/15501.html 什么?又要测试包! 做iOS开发几年了,每天除了码代码,改Bug之外,最让我烦恼的莫过于测试的妹子跑 ...
- 游戏案例|Service Mesh 在欢乐游戏的应用演变和实践
作者 陈智伟,腾讯 12 级后台专家工程师,现负责欢乐游戏工作室公共后台技术研发以及团队管理工作.在微服务分布式架构以及游戏后台运维研发有丰富的经验. 前言 欢乐游戏工作室后台是分布式微服务架构,目前 ...
- Apache执行Python脚本
由于经常需要到服务器上执行些命令,有些命令懒得敲,就准备写点脚本直接浏览器调用就好了,比如这样: 因为线上有现成的Apache,就直接放它里面了,当然访问安全要设置,我似乎别的随笔里写了安全问题,这里 ...
- SQL Server镜像自动生成脚本
SQL Server镜像自动生成脚本 镜像的搭建非常繁琐,花了一点时间写了这个脚本,方便大家搭建镜像 执行完这个镜像脚本之后,最好在每台机器都绑定一下hosts文件,不然的话,镜像可能会不work 1 ...
- 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- 探真无阻塞加载javascript脚本技术,我们会发现很多意想不到的秘密
下面的图片是我使用firefox和chrome浏览百度首页时候记录的http请求 下面是firefox: 下面是chrome: 在浏览百度首页前我都将浏览器的缓存全部清理掉,让这个场景最接近第一次访问 ...
- 第一个shell脚本
打开文本编辑器,新建一个文件,扩展名为sh(sh代表shell),扩展名并不影响脚本执行,见名知意就好. #!/bin/bash echo "Hello World !" &quo ...
随机推荐
- 转 AI教程 logo
版权申明:本文原创作者飞屋工作室,感谢飞屋工作室的原创分享! 这篇AI制作标志教程是一个非常实用的教程.通过这个教程飞特的朋友们将会学习到AI制作标志的流程和标志的创作思路.非常实用.推荐过来和飞特的 ...
- HDOJ-ACM1009(JAVA) (传说中的贪心算法)分为数组实现 和 封装类实现
转载声明:原文转自:http://www.cnblogs.com/xiezie/p/5564311.html 这个道题有几点要注意的: 数组存放的类型:float或double 打印的格式:(如果只是 ...
- MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN -摘自网络
在Membership系列的最后一篇引入了ASP.NET Identity,看到大家对它还是挺感兴趣的,于是来一篇详解登录原理的文章.本文会涉及到Claims-based(基于声明)的认证,我们会详细 ...
- hdu4421-Bit Magic(2-SAT)
题意 根据图中公式由A[]构造B[][],现在给你B,问你存不存在一个数组A使之成立. 题解:对于每一位进行2-sat求解. 比赛半个小时时间,没做出来…… 一直T. 因为本身对算法不确定,所以也不知 ...
- 第三百四十七天 how can I 坚持
下班的时候眼皮就一直在跳,今天意志好消沉,以后还是少说话,多说不宜啊.. 挣脱束缚,无论怎样,对于生命,什么都是次要的,不要想太多. 最近事比较多,应该是累了,睡一觉 应该就好了. 睡觉,晚安.
- 【MySQL】查看MySQL配置文件路径及相关配置
(1)关于配置文件路径 有时候,我发现虽然尝试修改了配置文件的一些变量,但是并没有生效.后来才发现原来是因为修改的文件并非MySQL服务器读取的配置文件. 如果不清楚MySQL当前使用的配置文件路径, ...
- ms-class的进化
ms-class是avalon用得最多的几个绑定之一,也正因为如此其功能一直在扩充中.根据时期的不同,分为旧风格与新风格两种. 旧风格是指正在ms-class后面跟着类外,然后在绑定值中添加表达式,即 ...
- Codeforces Round #368 (Div. 2) C. Pythagorean Triples(数学)
Pythagorean Triples 题目链接: http://codeforces.com/contest/707/problem/C Description Katya studies in a ...
- Vmware 虚拟的Linux系统如何与宿主主机共享上网
学校局域网内的机器是经过一个计费登陆客户端Gmon上网的,我前两天刚用Vmware虚拟了一个Linux Guest OS 用作测试用,在Vmware的VM>>Settings 里 ...
- Jsch
JSch is a pure Java implementation of SSH2. JSch allows you to connect to an sshd server and use por ...