安卓网游排行榜什么网游可以卖钱? 哪里要呢?

经典网游 ◆
经典游戏 ◆
装机必备 ◆
热门礼包 ◆
本类上升最快
显示方式: 列表 详细
简介:画一个圈圈住相同颜色的球即可得
简介:这是一款经典的经营游戏。
简介:一款可爱的虚拟鸟商店的虚拟宠物游戏
简介:此款游戏是一款巨龙题材的养成游戏。
简介:一款模拟经营的娱乐游戏
简介:欢迎来到美味餐厅!这次艾米莉经营的是一个路边餐厅,不能继续她的城市美食之旅。你能使艾米莉的餐厅再次成为声名远扬吗?
美味餐厅系列第4作,艾米莉又回来了,我们将和她一起共同谱写新的餐厅序曲。游戏画面延续这个系列清新明快的风格,各种图象绘制的精致细腻、卡通感十足,女主角艾米莉的造型也给玩家留下了深刻的印象。不同主题的餐厅将表现出多元化的场景环境。
故事背景:在艾米莉和哥哥Francois的旅行途中,汽车发生了事故,身无分文的他们来到了汽车餐厅,好心的老板Betty收留了他们,并且同意他们在餐厅中打工来赚取汽车的修理费用。就这样,
简介:【提醒】请按指引解锁软件
《模拟卡车驾驶3》是一款卡车模拟应用,主要就是模拟卡车在驾驶过程中的停车、拐弯等等,类似于考驾照时的驾车模拟。操作方面为了仿真当然是和真实货车一样的操作。
简介:2013年最火爆模拟经营游戏,全新的经营管理模式引发最火热的全民经商浪潮。新颖的游戏理念,完美融合公司发展、董事长养成、员工晋升和店铺营业等多样玩法。炒股、航海、参选官员,专为成功人士打造最真实、最刺激、最爽快的商业天地!
精美华丽的游戏界面——精心设计,专为用户量身定制的新版界面,合理布局每一个画面尺寸,操作便捷,线条流畅。为你带来更加舒适的游戏感官体验。
微妙细致的场景动态效果——城市场景中引入大量动态效果,包括天气变化、建筑、车辆、行人等清晰可见,栩栩如生,塑造身临其境的游戏感觉,还原更真实熟悉的现实世界。
高度自由的发展模式——三十六种店铺、十八种建筑任意搭配,彻底告别固定模式的思维方式,随意构建自己心中最壮观的的商业发展区。
花样繁多的店铺建筑外观——九大行业各有不同,每种行业的店铺和建筑都对应一个独特的外观造型。告别千篇一律带来的审美疲劳。当店铺规模成长后,外观更会即时随之变化,让你的街区更加雄伟大气!
极具真实感的模拟经济环境——期货市场、国际贸易、垄断控股,一切商业元素应有尽有。尽展你傲人的商业才华,将你的对手统统踩在脚下。
自创多人竞技模式——组队多人副本'决战商海'与多人PK团战'商会经济'。N重赛马类博弈论选择的全新诠释,多人互动,更加精彩。
同你的战友一起横扫群雄,决战商海!
多元化的城市规划发展——游戏中最具深度的高端玩法,凸显你智慧与能力的挑战!利用各不相同的店铺和建筑类型,通过巧妙的规划合理布局,就可有效规避竞争,最大限度发挥建筑的价值,让收入成倍提升。
最新更新内容
开放新系统行业协会,十大协会会长联手合奏政治经济最强音符,更可参选商联主席,万人之上、唯我独尊,霸气十足!
简介:想不想来个浪漫的沙滩晚餐?
驾驶你的跑车到美国迈阿密的岛屿,实现你的这一愿望!
这是一个非常具有挑战性的赛道海滩!
想象在一个非常美丽和性感的海滩,如果你想有一个疯狂的驱动器,那么这对你绝对是一个挑战。只要使用加速度计来转,触摸屏幕右侧的加快,想开到哪儿就到哪儿。
产品特点:
- 卓越的3D海滩边的轨道!
- 基于物理的赛车游戏
- 技术和非常快速的阶段
- 直观的方向盘(带加速计)
简介:忍者村 Ninja Village出产小商品,忍者们利用采集而来的资源与旅客交换钱物,用以扩充军力和提升装备,你要帮助他们合理地规划,运营起这个奇特的生态环境。
简介:考古为题材的创意类游戏
简介:《天堂岛Ⅱ》是一个充满了阳光和海水的地方,你所有的游客终极的向往之地,也是你的机遇之地。在这里,你需要发展自己的商业帝国,来满足游客们最基本的需求,并逐步提高你的层次,为游客们创造出更舒适的旅游环境。你可以创建各种娱乐中心,也可以开设赌场,餐厅和迪斯科舞厅来吸引他们消费。
【游戏任务】
- 在天堂岛上建造各种建筑和设施;
- 维护你的建筑,并提高它们的水平;
- 扩大你的商业帝国,购置新的土地和海域,创造出新的发展空间和利润;
- 创建各种独特的建筑和岛屿;
- 与你的商业伙伴进行竞争;
【游戏特色】
- 非常漂亮的游戏图形界面;
- 让你上瘾的游戏;
- 有各种设置和建筑,以及装饰和植物可以选择;
- 超过130种奖励和成就;
简介:一款经营类的模拟游戏
简介:独特英雄升级、技能系统,完胜其他塔防游戏
简介:《米高部落 The Meego》是一款模拟经营类游戏。欢迎来到Meego部落!这是一群居住在火山口上的神秘精灵,它们需要你的指引来建立伟大的部落!
【游戏特点】
-陪伴每个Meego见证他们的生老病死,成家立业
-随心所欲打造一个只属于你的部落
-收集各种奇特的工具和配饰
-五花八门的工作,让Meego不断成长
-推送Meego到好友的部落,获得额外资源和稀有道具
-独一无二的生肖石系统,收集珍稀的生肖石以赋予Meego更强大的能力
【注意事项】
游戏需要联网
简介:一款农场经营模式的游戏,考验你的经营策略!
简介:这是一款模拟经营类游戏。
简介:你将有机会经营一座海岛牧场
简介:一款经营模拟类的游戏,体验惊险刺激的海盗世界!
简介:一款有趣的据称为史上最小的经营类游戏
简介:"一款饭店经营模拟游戏,开发新菜和食材探险,努力成为地域NO.1的人气食堂,努力做到顾客的评价:好吃,太好吃了。
首先要准备桌椅和器具,做好迎接客人的准备。吃料理的客人,会根据店的气氛和待客给予点数得到高分后会从客人那里得到(餐桌礼仪研究会)(儿童礼物或者儿童套餐吧)(宇宙饮料试饮会)等新的服务提案。开发新店的话店的人气会迅速上升。培养店员,做出好吃的料理,让大家吃的满意吧。也可以经营多个店铺,是非常完美的模拟经营游戏。"
简介:煮热狗 汉堡 烹制美味 烧烤 挑战 疯狂
简介:嘿,来歌菲娅的厨房过一把大厨瘾!
简介:这是一款经营养成类游戏。
简介:根据著名动画南方公园而改编的游戏,快来加入啊!
简介:控制工人在传送带上移动,帮助工人制造出美味可口的面包。
简介:一款新型的日式站酒吧经营类游戏,中文版
简介:一款养马的游戏,管理你的牧场,训练赛马完成动人心魄的比赛!建立培养设施。建设你的牧场,吸引更多的游客,你可以在牧场附件开些小店来吸引客人。寻找上好的赛马,好好培养他们,为你赢得比赛获取奖金。快来体验吧!
简介:"守卫基地,升级武器并摧毁敌军! 一款充满RPG元素的塔防游戏。玩家必须守卫基地,阻止来自敌军吉普车,坦克和直升机的进攻。游戏操作非常简单,点击屏幕,控制基地的炮塔上下移动,炮塔会自动攻击敌军;游戏中的炮塔还有一块防御区在保护,在防御区敌军的炮火完全失效。游戏内置丰富的升级系统,开玩笑蛮高的一款塔防游戏...
游戏特点:
1.内置秘密武器
2.电磁波攻击可以让敌军的车辆瘫痪
3.防御区可以保卫基地
4.核弹秒杀战场上所有的敌人"
简介:一款模拟经营考验想象力跟创造力的休闲游戏后使用快捷导航没有帐号?
查看: 247|回复: 6
情在指尖荡漾
Lv.3, 积分 149, 距离下一级还需 101 积分
UID帖子威望0 多玩草0 草元宝
如——————题
情在指尖荡漾
Lv.3, 积分 149, 距离下一级还需 101 积分
UID帖子威望0 多玩草0 草元宝
沙发上,希望别沉下去啊!谢谢了亲们
就像风一样
Lv.4, 积分 432, 距离下一级还需 568 积分
UID帖子威望0 多玩草0 草元宝
不明觉厉··
新人欢迎积分1 阅读权限40积分528精华0UID帖子金钱1200 威望0
Lv.4, 积分 528, 距离下一级还需 472 积分
UID帖子威望0 多玩草0 草元宝
AQL大白虫子出光荣腿,楼下还知道什么地方
Lv.5, 积分 1212, 距离下一级还需 1288 积分
UID帖子威望0 多玩草0 草元宝
AQL 据说那里有好东西
死神的承诺
Lv.3, 积分 85, 距离下一级还需 165 积分
UID帖子威望0 多玩草0 草元宝
哦去有个位置啊&&我抢
死神的承诺
Lv.3, 积分 85, 距离下一级还需 165 积分
UID帖子威望0 多玩草0 草元宝
慢一步&&没有了 我哭啊&&话说这是个什么故事
需要金钱:1100
Powered by
手机盒子客户端点击或扫描下载什么网游练级的,练级能卖钱的!!!详细点,不要《问道》!_百度知道
什么网游练级的,练级能卖钱的!!!详细点,不要《问道》!
我有更好的答案
按默认排序
,地下,天龙八部
正常只要是网游基本有人买号
不过还是玩比较热门的 这样价就更高
我朋友的朋友
以前半个月练到129卖了1500
你看这个咋样,魔域,,,
梦幻西游?
其他类似问题
网游的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁使用libgdx开发Android游戏(1):一天内创建工作原型 - 博客 - 伯乐在线
& 使用libgdx开发Android游戏(1):一天内创建工作原型
| 分类: ,
| 标签: , ,
在本文中,我会间接的讲解构建一个游戏引擎和组件的模块,而且我将演示如何使用库快速开发一个游戏原型。
你将学到:
创建一个非常简单的2D射击平台游戏。
一个完整的游戏架构是什么样的。
在不了解OpenGL的情况下如何使用OpenGL的2D图形技术。
不同的实体如何组成了游戏和在游戏的世界中它们是如何联系在一起的。
如何为游戏加入音效。
如何在桌面构建游戏并发布到Android上,是的,就是那种魔法。
创建游戏的步骤
有一个游戏的想法
把你想象中的游戏画面以及样子在纸上简单画出一些场景。
分析思路,不断调整迭代几个版本,决定游戏在初始版本中应该有哪些东西
选择一种技术然后开始设计原型
开始进行编码,创建游戏的资源
试玩-测试,不断改进和持续小步前进进行直至完成
美化并发布
因为这是一个为期一天项目,时间非常有限而且我们的目标是学习制作游戏的技术,而不是实际的流程。出于这个目的,我冒昧的借鉴了其他游戏的思路,将重点放在这一过程的技术方面。
我在很大程度上借鉴了一个叫做 的游戏,它是制作的一个小的精品游戏。它是一个很简单的射击平台游戏,风格简单并且有街机的感觉。
这个游戏的想法是带领我们的英雄杀掉敌人,躲开其他试图杀死我们的东西来冲过关卡。
操作也非常简单,方向键向左或向右移动英雄,Z是跳跃,X是发射激光。按住跳跃键的时间越长,英雄跳的越高。可以在空中改变方向和射击。稍后我们将会把这些东西移动到Android平台。
接下来的步骤(2和3)可以跳过,因为有现成的游戏,所以我们已经做完了。
启动Eclipse
这是我们的起点,我会使用库创建游戏。为什么是libgdx?它是开发游戏最好的类库(在我看来),使开发游戏很容易而不需要知道太多相关的底层技术。它允许开发者在桌面上创建自己的游戏,不需要任何更改就可以部署到Android上。它提供了游戏中使用的所有元素,并且隐藏了与特定的技术和硬件打交道的复杂度。渐渐的这一点会变得越来越明显。
建立项目的简单方法
我们将使用的工具。建议按照libgdx的wiki页面上介绍的步骤来,但是我为星际守护提供一个快速的解决方法。
首先下载可执行的jar文件:。
双击运行,如果不成功,尝试在jar的下载路径下执行命令 :
java -jar gdx-setup-ui.jar
你将看到初始化画面。
点击 创建按钮,使用显示的值完成接下来的画面。
另外,还要确保为项目选择了合适的目标。
点击高亮的按钮自动下载最新版本的libgdx(稳定版),等待LibGDX 文字变绿。
一旦变绿,点击右下角的 Open the generation screen 按钮。
在接下来的窗口中,点击 启动按钮等待完成。成功的标志是显示”全部成功!”。
项目已经就绪,可以从被选择创建的目录导入到Eclipse中。
在Eclipse中,单击 文件-&导入…-&通用-&工作区间的工程,并且指向项目所在目录。
单击 完成会展现出可以被导入的项目清单,将它们全部导入。
单击完成,所有都设置完毕。
困难的设置工程(手动)
首先我们需要下载类库。
访问下载文件ibgdx-nightly-latest.zip,解压缩。
在Eclipse中创建一个简单的Java工程,我们叫它 star-assault。
创建工程的时候使用默认配置,右击它,选择 新建-&文件夹,创建一个名为libs的目录。
从解压开的libgdx-nighly-latest目录中,将gdx.jar文件拷贝到新创建的libs目录。同样把gdx-sources.jar文件拷贝到libs目录。它在解压后的gdx目录的sources 子目录下。在eclipse中,你可以简单的把jar文件拖拽到你的目录中做到这点。如果你使用资源管理器、查找器或者其他方式拷贝,不要忘记按F5刷新你的Eclipse项目。
这个结构应该像下图:
添加gdx.jar作为项目的依赖。具体做法是右击工程的名称,选择 属性。在这个画面上选择 Java Build Path,单击 Libraries 选项卡,单击 Add JARs…,进入libs 目录选择 gdx.jar,然后单击确定。
为了能够访问gdx 源代码并能够轻松调试游戏,添加dx.jar文件的源码是一个好主意。要做到这一点,展开gdx.jar节点,选择 Source attachment,单击 Edit…,然后 Workspace…,选择gdx-sources.jar单击确定直到关闭所有的弹出窗口。
使用libgdx建立项目的完整文档可以在官方的上找到。
这个项目是游戏的核心项目。它将包含游戏的机制,引擎,其他所有事情。我们还需要创建2个项目,我们的2个目标平台的基本启动器。一个用于Android,一个用于桌面。这些项目会极其简单,只包含在各自平台运行游戏所需的依赖关系。把他们当做包含main方法的类。
为什么我们需要将这些分为独立的项目?因为libgdx 隐藏了处理底层操作系统(图形、音频、用户输入、文件I/O等)的复杂度,每个平台都有一个具体的实现,我们只需要包含目标平台上需要的实现(绑定)。也因为应用程序生命周期,资源加载(加载图形、音频等)和应用程序的其他常见方面被大大的简化,平台的特定实现位于不同的JAR文件中,只有我们目标平台需要的文件才会被添加。
像前面的步骤一样创建一个简单的Java工程,命名为star-assault-desktop。同时参照创建libs目录的步骤。这次从下载的压缩文件里面所需的jar文件是:
gdx-natives.jar,
gdx-backend-lwjgl.jar,
gdx-backend-lwjgl-natives.jar.
像前面的项目一样,也要把这些jar文件添加到项目的依赖中去。(右击project -& Properties -& Java Build Path -& Libraries -& Add JARs,选择这3个jar文件然后单击确定)。
我们还需要把star-assault项目添加到依赖中。要做到这一点,单击 Projects选项卡,单击 添加,选中star-assault项目然后单击确定。
重要! 我们需要设定star-assault项目为传递依赖,这意味着这个项目的依赖关系被当成依赖于本项目的项目依赖关系。要执行此操作:右击主项目, Properties -& Java Build Path -& Order and Export,选择gdx.jar然后单击确定。
Android版本
为此,你需要安装。
在Eclipse中创建一个Android工程: File -& New -& Project -& Android Project。
将它命名为star-assault-android。对于构建版本,选择”Android 2.3″。指定包名为”net.obviam”或者其它你喜欢的名字。在接下来的”创建Activity”中输入StarAssaultActivity,点击 完成。
进到项目的路径下,创建 libs子文件夹(可以在eclipse中完成操作)。从 nightly zip文件中,拷贝gdx-backend-android.jar文件和the armeabi 和armeabi-v7a 目录到新创建的libs目录下。
在eclipse中,右击project -& Properties -& Java Build Path -& Libraries -& Add JARs,选择 gdx-backend-android.jar,单击OK。
再次单击 Add JARS,选择主工程下(star-assault)的gdx.jar,单击OK。
单击 项目标签,单击 Add,选中主工程然后单击两次OK。
下面是展示的结构:
对于ADT发布版17或者更新版本,gdx jar文件需要被显示的标记为导出。
具体做法:
在Android项目上单击
选择 Properties
选择 Java Build Path (第一步)
选择 Order and Export(第二步)
选择所有的引用,例如gdx.jar, gdx-backend-android.jar,主项目。(第三步)。
下面的图像显示了最新的状态。
另外,在查找有关该问题的详细信息。
共享资源(图片、声音和其他数据)
因为游戏需要在Android和桌面版本上保持一致,但是每个版本又需要从独立的项目构建。我们需要将图像、声音和其他数据放到一个共享位置。理想的情况是把这些内容放到主项目中,因为Android和桌面版都包含主项目。但是因为Android对于如何存放这些文件有严格的规则,我们必须把资源放到那里。它就在Android项目自动创建的资源目录下。在eclipse中,有一种链接目录的方式,就像 linux/mac上的符号链接或者Windows下的快捷方式。若要将Android项目中的资源目录链接到桌面版本,请执行以下操作:
右击 star-assault-desktop 项目-&Properties -& Java Build Path -& Source tab -& Link Source… -& Browse… ,浏览到star-assault-android项目的assets目录,然后单击完成。你也可以扩展变量列表而不必浏览到assets 目录。建议将项目设置为文件系统无关。
另外,确保assets 目录被添加到源文件目录中。具体做法:在eclipse中(桌面版本)的assets目录上右击,选择 Build Path -& Use as Source Folder。
本阶段我们的设置已经准备完毕,可以继续进行游戏的工作。
计算机应用程序是一个运行在机器上的软件,它启动,做一些事情(甚至什么都不做),并在一种或另一种条件下停止。电脑游戏是一种特殊的应用,它在”做一些事情”的部分做的是游戏。所有应用的开始和结束都基本上是一样的。另外,游戏有一个非常简单的基于连续循环的架构。可以在这些地方找到更多关于和的东西。
由于有libgdx,我们的游戏像在剧场上演戏剧一样拼凑起来。所有你需要做的就是,把游戏想象为一个舞台剧。我们将定义舞台,演员,角色和行为,但我们把编剧委托给玩家。
为了创建游戏我们需要采用下面的步骤:
1. 启动应用
2. 载入所有的图片和声音,存储在内存里
3. 为我们的游戏创建舞台,利用演员和它的行为(它们之间的交互规则)
4. 将控制权交给玩家
5. 创建基于从控制器接收的输入来操作演员在舞台上的行为的机制
6. 决定游戏何时结束
7. 结束显示
它看起来很简单并且它确实是很这样。我将会在它们出现的时候介绍概念和元素。
为了创建游戏,我们仅仅需要1个类。
让我们在star-assault项目中创建StarAssault.java,这个项目中的每个类都有2个例外。
package net.obviam.
import com.badlogic.gdx.ApplicationL
public class StarAssault implements ApplicationListener {
public void create() {
// TODO Auto-generated method stub
public void resize(int width, int height) {
// TODO Auto-generated method stub
public void render() {
// TODO Auto-generated method stub
public void pause() {
// TODO Auto-generated method stub
public void resume() {
// TODO Auto-generated method stub
public void dispose() {
// TODO Auto-generated method stub
只是实现了gdx中的ApplicationListener接口,eclipse 会自动生成需要实现的方法的存根代码。
这些都是我们需要实现的应用程序生命周期的方法。我们简单的认为需要为Android或者桌面设置所有的代码,来初始化OpenGL上下文和所有那些枯燥(困难的)的任务。
方法create()首先被调用。发生时机是该应用程序已准备就绪,可以开始加载我们的资源、创建舞台和演员。想象成在所有的东西都被运到那里,并准备好后为演出搭建舞台。根据剧场的位置和你到达的方式,物流肯定是个噩梦。你可以通过人力、飞机、卡车进行运输…我们不知道。我们在其中所有东西都已经就绪,可以开始着手组装它们。这就是libgdx帮我们完成的事情。不区分平台的帮我们搬运并交付东西。
resize()方法在每次绘画界面改变的时候被调用。这让我们有机会再继续播放前重新安排代码。例如窗口(如果游戏有窗口)调整时会触发此事件。
每个游戏的核心都是render()方法,它无非就是一个无限的循环。这个方法被不断调用,直到游戏结束或者我们终止游戏。这是游戏的过程。
注:对于电脑来说游戏结束与其他程序是不一样的。所以它只是一个状态。程序是在游戏结束的状态,但它仍然在运行。
当然,游戏可以被暂停中断,也可以恢复。每当Android或者桌面应用程序进入到后台模式时pause()方法会被调用。当应用程序重新恢复到前台时resume()方法被调用。
当游戏完成后,应用程序被关闭时,dispose()方法被调用,这是时候做一些清理工作了。它类似于演出已经结束,观众已经离开,舞台将要被拆除。不再回来了。更多关于生命周期的内容可以在找到。
让我们开始实际的开始做游戏。第一个里程碑是有一个我们的家伙可以移动的世界。这个世界是由水平面组成,每个平面都包含地形。地形无非是一些我们的家伙无法通过的障碍物。
目前为止在游戏中确定演员和实体很容易。
我们的家伙(让我们叫它Bob,libdgx有关于Bob的教程)和障碍物组成了这个世界。
玩过星际守护的话,就会发现Bob有几个状态。当我们不操作的时候,Bob处于空闲状态。他也可以移动(在两个方向),也能跳。此外,当他死了,就不能做任何事情了。Bob在任何时候的状态只能是4个确定的状态中的一个。也还有其他的状态,但我们现在不考虑。
Bob的状态:
空闲 – 当没有移动或者跳跃并且或者
移动 – 以恒定的速率向左或向右
跳跃 – 也是面向左或者右并且有高有低
死亡 – 它甚至不可见或者再生
障碍物是其他的演员。为简单起见,我们也只有几个障碍物。水平面由放置在一个二维空间内的障碍组成。为简单起见,我们将使用一个网格。
将Star Guard的开局变为障碍物和Bob的结构,将看起来像这样:
上面一个图片是原始的,下面的是我们的世界的表示。
我们已经想像出了世界,但我们需要工作在一个有意义的测量系统。为简单起见,我们把世界里的一个块是当成1个单位宽、1个单位高。我们可以用米来表示使其更简单,但因为Bob是半个单元,这让他才半米高。让我们认定游戏世界中的4个单位为1米,因此Bob的身高是2米。
这一点很重要,因为我们将计算鲍勃跑动的速度等等,我们需要知道我们在做什么。
让我们来创建世界。
我们的主角是Bob。
Bob.java类像这样:
package net.obviam.starassault.
import com.badlogic.gdx.math.R
import com.badlogic.gdx.math.Vector2;
public class Bob {
public enum State {
IDLE, WALKING, JUMPING, DYING
static final float SPEED = 2f;
// unit per second
static final float JUMP_VELOCITY = 1f;
static final float SIZE = 0.5f; // half a unit
position = new Vector2();
acceleration = new Vector2();
velocity = new Vector2();
bounds = new Rectangle();
state = State.IDLE;
facingLeft =
public Bob(Vector2 position) {
this.position =
this.bounds.height = SIZE;
this.bounds.width = SIZE;
#16-#21行定义了Bob的属性。这些属性值定义了任何时间Bob的状态。
position – Bob在世界中的位置。用世界坐标系来表示(后面将详细讨论)。
acceleration – 决定Bob跳跃时的加速度。
velocity – 用来进行计算并用在Bob移动上。
bounds – 游戏中的每个元素都会有一个边框。这只是一个矩形,以便知道Bob是否撞到了墙,是否被被子弹杀死或是否击中敌人。其将被用于碰撞检测。想像成在玩方体。
Bob的当前状态。当我们发出步行的动作,状态将变成运动,基于这个状态,我们知道如何绘制屏幕。
facingLeft –
代表Bob的朝向。作为一个简单的2D平台游戏,我们只有两个方向,左和右。
#12-#15 行定义了一些常量,我们将用它来计算在世界中的速度和位置。这些将在后面进行调整。
我们还需要一些障碍来组成世界。
Block.java 看起来像这样:
package net.obviam.starassault.
import com.badlogic.gdx.math.R
import com.badlogic.gdx.math.Vector2;
public class Block {
static final float SIZE = 1f;
position = new Vector2();
bounds = new Rectangle();
public Block(Vector2 pos) {
this.position =
this.bounds.width = SIZE;
this.bounds.height = SIZE;
障碍只不过是摆在世界上的一堆矩形。我们将使用这些障碍来构建地形。我们有一个简单的规则。没有任何东西可以穿过它们。
libgdx提示
你可能已经注意到我们使用了libgdx的Vector2类型。因为它为我们提供了处理欧几里得向量需要的所有事情,这使得我们的生活容易了很多。我们将使用向量来定位实体,计算速度,并左右移动东西。
关于坐标系统和单位
和现实世界一样,我们的世界也有尺寸。在一个平面上想象一个房间。它具有宽度、高度和深度。我们把它当做是二维的,不考虑深度。如果房间是5米高,3米宽,我们可以说我们在一个度量系统里描述房子。很容易想象在房子中间放置1个1米宽1米高的桌子。我们无法通过该桌子、穿过它,我们需要跳到它的上面,走1米,然后跳下来。我们可以使用多个桌子来创建一个金字塔,并在房间里制作一些奇怪的设计。在我们Star Assault的世界里,世界代表了现实世界的房间、障碍物代表了现实世界的桌子、单位代表了现实世界的米。
如果我每小时跑10KM,转换一下就是2.每秒(10 * 1000 / 3600)。将这些转换成Star Assault的坐标,要代表10KM/h的速度,我们将使用2.7单位/秒。
检查下面的利用世界坐标系表示的图表,里的有带着边界的盒子和Bob。
红色的方块是障碍物的边界框。绿色的方块是Bob的边界框。空的方块只是个空。网格仅供参考。这就是我们将要模拟的世界。坐标系的原点位于左下角,所以向左走10,000单位/小时意味着Bob位置的x坐标将以2.7单位每秒的速度减少。
还要注意的是成员的访问权限是包默认权限,模型在一个单独的包里。我们要想从引擎中访问,必须要创建存取方法(getter和setter)。
作为第一步,我们将只创建世界作为一个硬编码的小房间。它有10单位宽和7单位高。我们将像下面显示的图片一样放置Bob和障碍物。
World.java看起来像这样:
package net.obviam.starassault.
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.A
public class World {
/** The blocks making up the world **/
Array blocks = new Array();
/** Our player controlled hero **/
// Getters -----------
public Array getBlocks() {
public Bob getBob() {
// --------------------
public World() {
createDemoWorld();
private void createDemoWorld() {
bob = new Bob(new Vector2(7, 2));
for (int i = 0; i & 10; i++) {
blocks.add(new Block(new Vector2(i, 0)));
blocks.add(new Block(new Vector2(i, 7)));
if (i & 2)
blocks.add(new Block(new Vector2(i, 1)));
blocks.add(new Block(new Vector2(9, 2)));
blocks.add(new Block(new Vector2(9, 3)));
blocks.add(new Block(new Vector2(9, 4)));
blocks.add(new Block(new Vector2(9, 5)));
blocks.add(new Block(new Vector2(6, 3)));
blocks.add(new Block(new Vector2(6, 4)));
blocks.add(new Block(new Vector2(6, 5)));
它只是一个简单的世界的实体的容器类。当前实体是障碍物和Bob。在构造器中,障碍物被添加到blocks数组,并且Bob被创建。暂时这一切都是硬编码的。请记住,原点在左下角。
创建游戏并显示世界
为了把世界渲染到屏幕上,我们需要创建一个画面,并让它来渲染这个世界。在libgdx中,有一个便捷的叫做Game的类,我们将重写StarAssault类作为libgdx的Game类的子类。
一个游戏可以包含多个画面。甚至我们的游戏将包含3个画面。 开始游戏画面, 播放游戏画面, 游戏结束画面。每个画面都只是关注自己的事情而不关注其他画面的。例如 开始游戏画面将会包含菜单选项 开始 和 退出 。它有2个元素(按钮),它关注的是处理这些元素上的 单击/触控 。它始终显示这两个按钮,如果我们单击/触控 开始 按钮,它会通知主游戏加载 播放游戏画面 并退出当前画面。播放画面将运行我们的游戏,并处理相关的所有事情。一旦游戏满足结束状态,它告诉主游戏过渡到游戏结束画面,后者的唯一目的是显示最高得分,并监听重新开始的单击事件。
让我们重构代码并暂时只创建游戏的主画面。我们将会跳过游戏的开始和结束画面。
GameScreen.java
package net.obviam.starassault.
import com.badlogic.gdx.S
public class GameScreen implements Screen {
public void render(float delta) {
// TODO Auto-generated method stub
public void resize(int width, int height) {
// TODO Auto-generated method stub
public void show() {
// TODO Auto-generated method stub
public void hide() {
// TODO Auto-generated method stub
public void pause() {
// TODO Auto-generated method stub
public void resume() {
// TODO Auto-generated method stub
public void dispose() {
// TODO Auto-generated method stub
StarAssault.java就变得非常简单。
package net.obviam.
import net.obviam.starassault.screens.GameS
import com.badlogic.gdx.G
public class StarAssault extends Game {
public void create() {
setScreen(new GameScreen());
GameScreen 实现了Screen接口,该接口非常像ApplicationListener,但是添加了2个重要的方法。
show() – 当主程序激活此画面的时候调用
hide() – 当主程序激活其他画面的时候调用
StarAssault 只实现了一个方法,create()方法只是激活了新实例化的GameScreen。换句话说,它创建了它,调用show()方法,随后每个周期将调用它的render()方法。
GameScreen变成了我们下一部分的焦点,因为它是游戏存活的地方。记住游戏的循环是render()方法。但是为了渲染某些东西我们首先需要创建一个世界。世界可以在show()方法中创建,因为没有其他的画面会打断我们的游戏。目前,游戏画面仅在游戏开始的时候显示。
我们会为类添加2个成员变量,并实现render(float delta) 方法。
private WorldR
/** Rest of methods ommited **/
public void render(float delta) {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
renderer.render();
world属性是World的实例,它保存了障碍物和Bob。
renderer是一个将world 绘制/渲染到画面的类(不久我将解释)。
方法 render(float delta)。
让我们创建WorldRenderer 类。
WorldRenderer.java:
package net.obviam.starassault.
import net.obviam.starassault.model.B
import net.obviam.starassault.model.B
import net.obviam.starassault.model.W
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicC
import com.badlogic.gdx.graphics.glutils.ShapeR
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeT
import com.badlogic.gdx.math.R
public class WorldRenderer {
private OrthographicC
/** for debug rendering **/
ShapeRenderer debugRenderer = new ShapeRenderer();
public WorldRenderer(World world) {
this.world =
this.cam = new OrthographicCamera(10, 7);
this.cam.position.set(5, 3.5f, 0);
this.cam.update();
public void render() {
// render blocks
debugRenderer.bined);
debugRenderer.begin(ShapeType.Line);
for (Block block : world.getBlocks()) {
Rectangle rect = block.getBounds();
float x1 = block.getPosition().x + rect.x;
float y1 = block.getPosition().y + rect.y;
debugRenderer.setColor(new Color(1, 0, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
// render Bob
Bob bob = world.getBob();
Rectangle rect = bob.getBounds();
float x1 = bob.getPosition().x + rect.x;
float y1 = bob.getPosition().y + rect.y;
debugRenderer.setColor(new Color(0, 1, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
debugRenderer.end();
该WorldRenderer只有一个目的。获取世界的当前状态,并呈现在屏幕上。它只有单独一个公共的render()方法,该方法被主循环调用(GameScreen中)。渲染器需要访问世界对象,所以我们在构造它的时候将world传入。对于第一步,我们将呈现元素(块和Bob)的边框来看看我们到现在有了什么。利用原生的OpenGL绘图是相当乏味,但libgdx自带的ShapeRenderer使这个任务变得很容易。
重要的行进行说明。
#14 – 声明world 为一个成员变量。
#15 – 声明一个OrthographicCamera,我们使用这台摄像机以垂直的角度看世界。当前世界还是比较小的,它只占了一个屏幕,但我们会有一个更广泛的空间,当Bob在里面移动的时候,摄像机会跟随着它。它类似于现实生活中的相机。更多关于正交投影的内容可以在找到。
#18 – 声明了ShapeRenderer。我们将用它来绘制实体的图元(矩形)。这是一个辅助渲染器,可以像画很多图元,比方说直线,矩形,圆。对于熟悉基于图形的画布的人来说,这应该很简单。
#20 – 构造器以world作为参数。
#22 – 我们创建了一个拥有10单位宽、7单位高视口的摄像机。这也就意味着在用块(宽=高=1)来填充屏幕的时候,X轴上显示10个框,Y轴上显示7个。
重要说明 这与分辨率无关。如果屏幕分辨率为480×320,这意味着480像素代表10个单位,所以一个框将是48个像素宽。这也意味着,320像素代表7个单位,所以在屏幕上盒框是45.7像素高。这不会是一个完美的正方形。这取决于纵横比。在我们这个例子里纵横比为10比7。
#23 – 这一行定位摄像机来看在房子的中间。默认情况下,它看在(0,0),这是房间的一角。相机的(0,0)在中间,就和一个普通相机一样。下图显示了世界和摄像机设置的坐标。
#24 – 相机内部的矩阵被更新。 在每次相机操作(移动,缩放,旋转等)的时候都必须调用update方法。 OpenGL被完美隐藏。
render() 方法:
我们将相机里的矩阵应用到渲染器。这是有必要的,因为我们已经定位了相机,我们希望它们是一致的。
#30 – 告诉渲染器我们要绘制矩形。
#31 – 我们要绘制块,所以要迭代访问世界里的所有块。
#32 – #34 – 提取每个块的边界矩形的坐标。 OpenGL要使用顶点(点),所以它绘制矩形时必须知道起点坐标和宽度。需要注意,我们在使用摄像机的坐标工作,它与世界坐标重合。
#35 – 矩形的颜色设置为红色。
#36 – 在X1,Y1位置利用给定的宽度和高度绘制矩形
#39 – #44 – 对于Bob做同样的操作。但这次的矩形是绿色的。
#45 – 我们让渲染器知道我们已经绘制完矩形。
我们需要将renderer和world添加到GameScreen(主循环),然后在行动中看到它。
像这样修改GameScreen。
package net.obviam.starassault.
import net.obviam.starassault.model.W
import net.obviam.starassault.view.WorldR
import com.badlogic.gdx.G
import com.badlogic.gdx.S
import com.badlogic.gdx.graphics.GL10;
public class GameScreen implements Screen {
private WorldR
public void show() {
world = new World();
renderer = new WorldRenderer(world);
public void render(float delta) {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
renderer.render();
/** ... rest of method stubs omitted ... **/
render(float delta)有3行代码。前2行代码用黑色清空屏幕,第3行代码简单的调用renderer的render()方法。
World 和 WorldRenderer 在画面显示的时候被调用。
为了在桌面和Android上测试,我们需要为这两个平台创建启动器。
创建桌面和Android的启动器
我们在开始的时候已经创建了2个项目,star-assault-desktop和star-assault-android,后者是一个Android项目。
桌面项目非常简单,我们需要创建一个拥有main方法的类,该方法实例化一个libgdx提供的application类。
在桌面项目中创建StarAssaultDesktop.java类。
package net.obviam.
import com.badlogic.gdx.backends.lwjgl.LwjglA
public class StarAssaultDesktop {
public static void main(String[] args) {
new LwjglApplication(new StarAssault(), &Star Assault&, 480, 320, true);
这就是了。第 #7 行做了所有事情。它实例化了一个新的LwjglApplication应用程序,传入一个新生成的StarAssault实例,它是一个游戏的实现。第二和第三参数告诉了窗口的尺寸。我选择了480×320,因为它是众多Android手机的支持的分辨率,我想就像它在桌面上。最后一个参数告诉libgdx使用OpenGL ES2。
作为一个普通的Java程序运行该应用,应该输出以下结果:
如果有错误,追溯一下,确保设置的正确并且遵循了所有的步骤,包括检查star-guard项目properties -& Build Path-&export选项卡内的gdx.jar。
Android版本
在 star-assault-android 项目里有一个单独的类,叫做StarAssaultActivity。
转到StarAssaultActivity。
package net.obviam.
import android.os.B
import com.badlogic.gdx.backends.android.AndroidA
import com.badlogic.gdx.backends.android.AndroidApplicationC
public class StarAssaultActivity extends AndroidApplication {
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
config.useAccelerometer =
config.useCompass =
config.useWakelock =
config.useGL20 =
initialize(new StarAssault(), config);
注意,新的activity 继承自AndroidApplication。
在 #13行,一个AndroidApplicationConfiguration对象对创建。我们可以设置有关Android平台的所有类型的配置。他们都不需要解释,但要注意,如果我们想要使用Wakelock,还需要修改AndroidManifest.xml文件。这要从Android中请求权限,以保持设备运行,防止我们不触碰的时候屏幕变暗。
添加以下行到AndroidManifest.xml文件里标签内的某个地方。
原文中没有此XML内容:(
另外,在 #17行,我们告诉Android使用的OpenGL ES 2.0。这意味着我们将只能在设备进行测试,因为仿真器不支持OpenGL ES 2.0。但如果是出了问题,把它改为false。
#18行初始化Android应用程序并运行。
有一个连接到eclipse的设备,应用就会被直接部署到上面,下面你可以看到一张在Nexus One上运行应用程序的照片。它看起来和桌面版本一样。
在这么短的时间里我们走了这么远真是相当令人印象深刻啊。注意使用MVC模式。它非常简单和有效。模型是我们想要显示的实体。视图是渲染器。视图把模型绘制到了屏幕上。现在,我们需要和实体(尤其是Bob)进行交互,我们将介绍一些控制器了。
要了解更多的MVC模式的东西,可以看看我的,或在网络上进行搜索。这是非常有用的。
到目前为止,一切都很好,但显然我们要使用一些适当的图形。 MVC的功能就派上用场了,我们将修改渲染,让它绘制图像,而不是矩形。
在OpenGL中显示图像是一个相当复杂的过程。首先,它需要被加载,变成纹理,然后被映射到被几何描述的表面。 libgdx使得这个过程变得非常简单。要硬盘中的图像转换成一个纹理只用1行代码。
我们将用2个图像,因此也就是2个纹理。一个纹理用于Bob,另一个用于块。我已经创建了这两个图像,块和Bob。 Bob是Star Guard里的家伙的山寨。这些都是简单的PNG文件,我将它们复制到 assets/images目录里。我有两个图像: block.png和bob_01.png 。最终,Bob将成为一个活动的形象,所以我加了数字后缀(为未来考虑)。
首先让我们稍微整理一下WorldRenderer,把矩形的绘制提取到一个单独的方法中,因为我们调试的时候将用到它。
我们需要加载这些纹理,并把它们渲染在屏幕上。
一起来看看新WorldRenderer.java。
package net.obviam.starassault.
import net.obviam.starassault.model.B
import net.obviam.starassault.model.B
import net.obviam.starassault.model.W
import com.badlogic.gdx.G
import com.badlogic.gdx.graphics.C
import com.badlogic.gdx.graphics.OrthographicC
import com.badlogic.gdx.graphics.T
import com.badlogic.gdx.graphics.g2d.SpriteB
import com.badlogic.gdx.graphics.glutils.ShapeR
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeT
import com.badlogic.gdx.math.R
public class WorldRenderer {
private static final float CAMERA_WIDTH = 10f;
private static final float CAMERA_HEIGHT = 7f;
private OrthographicC
/** for debug rendering **/
ShapeRenderer debugRenderer = new ShapeRenderer();
/** Textures **/
private Texture bobT
private Texture blockT
private SpriteBatch spriteB
private boolean debug =
private float ppuX; // pixels per unit on the X axis
private float ppuY; // pixels per unit on the Y axis
public void setSize (int w, int h) {
this.width =
this.height =
ppuX = (float)width / CAMERA_WIDTH;
ppuY = (float)height / CAMERA_HEIGHT;
public WorldRenderer(World world, boolean debug) {
this.world =
this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
this.cam.position.set(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f, 0);
this.cam.update();
this.debug =
spriteBatch = new SpriteBatch();
loadTextures();
private void loadTextures() {
bobTexture = new
Texture(Gdx.files.internal(&images/bob_01.png&));
blockTexture = new Texture(Gdx.files.internal(&images/block.png&));
public void render() {
spriteBatch.begin();
drawBlocks();
drawBob();
spriteBatch.end();
if (debug)
drawDebug();
private void drawBlocks() {
for (Block block : world.getBlocks()) {
spriteBatch.draw(blockTexture, block.getPosition().x * ppuX, block.getPosition().y * ppuY, Block.SIZE * ppuX, Block.SIZE * ppuY);
private void drawBob() {
Bob bob = world.getBob();
spriteBatch.draw(bobTexture, bob.getPosition().x * ppuX, bob.getPosition().y * ppuY, Bob.SIZE * ppuX, Bob.SIZE * ppuY);
private void drawDebug() {
// render blocks
debugRenderer.bined);
debugRenderer.begin(ShapeType.Line);
for (Block block : world.getBlocks()) {
Rectangle rect = block.getBounds();
float x1 = block.getPosition().x + rect.x;
float y1 = block.getPosition().y + rect.y;
debugRenderer.setColor(new Color(1, 0, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
// render Bob
Bob bob = world.getBob();
Rectangle rect = bob.getBounds();
float x1 = bob.getPosition().x + rect.x;
float y1 = bob.getPosition().y + rect.y;
debugRenderer.setColor(new Color(0, 1, 0, 1));
debugRenderer.rect(x1, y1, rect.width, rect.height);
debugRenderer.end();
我来讲一下重要的代码行:
#17 & #18 – 声明视口尺寸的常量,用于摄像机。
#27 & #28 – 声明2个纹理,将用于Bob和块。
#30 – 声明SpriteBatch,SpriteBatch 负责纹理的映射、显示等等。
#31 – 这是一个在构造函数中设置的属性,确定我们是否也需要渲染调试画面。记住,调试渲染只会渲染游戏元素的边框。
#32 – #35 – 这些变量都是正确显示元素所必须的。width和height保存了屏幕的像素大小,是操作系统在调整尺寸的步骤里传进来的。ppuX 和ppuY是每个单位长度的像素的数量。
因为我们将相机设定为在世界坐标中具有10×7的视口(这意味着我们横向显示10个框和垂直显示7个),最终结果是我们要处理像素,我们需要将这些值映射为实际的像素坐标。我们选择了在480 ×320分辨率下工作。这意味着,480水平像素等效为10个单位,意味着一个单位将包括屏幕上48个像素。
如果我们试图用相同的单位来表示高度( 48像素),我们得到336像素( 48 * 7 = 336)。但我们只有320像素,而我们想要显示完整的7块的高度。要垂直方向达到这种显示效果,我们得到垂直的1个单元是320 /7 = 45.71个像素。我们需要稍微扭曲一下图像以适应我们的世界。
这样做完全正常,OpenGL实现起来也非常容易。在我们改变电视的长宽比时也会发生这种情况,有时图像被拉长或压扁,以适应屏幕上,或者我们只是简单地选择削减图像保持纵横比的选项。
注意:在这里我们使用了float,即使屏幕分辨率为整数,OpenGL喜欢使用浮点数,我们就这样给它。 OpenGL的将计算出尺寸和放置像素的地方。
#36 – setSize (int w, int h) 方法在每次屏幕调整的时候被调用,它简单的计算(重新计算)单位的像素。
#43 – 构造函数稍微改变了一下,但它是非常重要的。它实例化SpriteBatch并加载纹理(#50行 ) 。
#53 – loadTextures()方法和它的名字一样:加载纹理。你看这是多么的简单。要创建一个纹理,我们需要传递一个文件处理器,它创建了一个纹理出来。在libgdx文件处理器非常有用,因为我们并不区分Android和桌面,我们只是指定要使用一个内部文件,它知道如何加载它。请注意,对于路径,我们跳过了assets,因为assets是源代码目录,这意味着该目录下的所有内容都会被拷贝到最终包的根目录下。这样assets类似于一个根目录。
#58 – 新的render()方法只包含几行。
#59 & #62 – 闭合SpriteBatch的渲染块/会话。 每次我们想通过SpriteBatch 来在OpenGL里渲染图像时,都需要调用begin(),渲染我们的东西,然后在结束的时候调用end()。必须要这样做,否则它无法工作。你可以在阅读更多关于SpriteBatch的东西。
#60 & #61 –简单地调用2个方法,首先呈现块,然后Bob。
#63 & #64 – 如果调试启用,调用该方法来渲染框。前面详细的介绍过drawDebug方法。
#67 – #76 – drawBlocks和drawBob方法类似。每个方法都使用纹理参数调用SpriteBatch的绘制方法。理解这一点很重要的。
第一个参数是纹理(从磁盘加载的图像)。
第二个和第三个参数告诉SpriteBatch显示图像的地方。注意,我们使用从世界坐标到屏幕坐标的转换。这里使用了ppuX和ppuY。你可以自己动手进行计算,看看图像应该显示在什么地方。SpriteBatch默认使用原点(0,0)在左下角的坐标系统。
就这些了。只是要保证你修改了GameScreen类,让resize方法调用render,同时还要把render的debug参数设置为true。
GameScreen中的修改。
/** ... omitted ... **/
public void show() {
world = new World();
renderer = new WorldRenderer(world, true);
public void resize(int width, int height) {
renderer.setSize(width, height);
/** ... omitted ... **/
运行该应用程序应该产生以下结果:
没有调试。
调试渲染。
太棒了!给它在Android上一试了,看看它的外观。
处理桌面端和Android的输入
我们已经走过了很长的路,但到目前为止,世界还是静止的,没有什么有趣的事情。要做一个游戏,我们需要添加输入处理,来拦截按键和触摸,并基于这些创建一些行动。
桌面上的控制模式非常简单。箭头键将控制Bob左右移动,Z是跳跃,X是发射武器。在Android上,我们将有不同的方法。我们将为这些功能指定一些按钮,并把它们放到屏幕上,通过按压相应区域,我们会认为某个键被按下。
遵循MVC模式,我们会将控制Bob和世界其他部分的类与模型和视图类分开。创建包net.obviam.starassault.controller,所有的控制器都放在那里。
对于一开始,我们将控制鲍勃通过按键。要发挥我们需要跟踪的4个按键的状态比赛:向左移动,向右移动,跳跃和射击。因为我们将使用2种类型的输入(键盘和触摸屏),实际事件需要被送入一个处理器可以触发动作。
每个动作都由事件触发。
向左移动的动作由左箭头键按下或屏幕的特定区域被触摸时的事件引发。
当Z键被按下时跳跃动作被触发等等。
让我们创建一个非常简单的控制器,叫做WorldController。
WorldController.java
package net.obviam.starassault.
import java.util.HashM
import java.util.M
import net.obviam.starassault.model.B
import net.obviam.starassault.model.Bob.S
import net.obviam.starassault.model.W
public class WorldController {
enum Keys {
LEFT, RIGHT, JUMP, FIRE
static Map&Keys, Boolean& keys = new HashMap&WorldController.Keys, Boolean&();
keys.put(Keys.LEFT, false);
keys.put(Keys.RIGHT, false);
keys.put(Keys.JUMP, false);
keys.put(Keys.FIRE, false);
public WorldController(World world) {
this.world =
this.bob = world.getBob();
// ** Key presses and touches **************** //
public void leftPressed() {
keys.get(keys.put(Keys.LEFT, true));
public void rightPressed() {
keys.get(keys.put(Keys.RIGHT, true));
public void jumpPressed() {
keys.get(keys.put(Keys.JUMP, true));
public void firePressed() {
keys.get(keys.put(Keys.FIRE, false));
public void leftReleased() {
keys.get(keys.put(Keys.LEFT, false));
public void rightReleased() {
keys.get(keys.put(Keys.RIGHT, false));
public void jumpReleased() {
keys.get(keys.put(Keys.JUMP, false));
public void fireReleased() {
keys.get(keys.put(Keys.FIRE, false));
/** The main update method **/
public void update(float delta) {
processInput();
bob.update(delta);
/** Change Bob's state and parameters based on input controls **/
private void processInput() {
if (keys.get(Keys.LEFT)) {
// left is pressed
bob.setFacingLeft(true);
bob.setState(State.WALKING);
bob.getVelocity().x = -Bob.SPEED;
if (keys.get(Keys.RIGHT)) {
// left is pressed
bob.setFacingLeft(false);
bob.setState(State.WALKING);
bob.getVelocity().x = Bob.SPEED;
// need to check if both or none direction are pressed, then Bob is idle
if ((keys.get(Keys.LEFT) && keys.get(Keys.RIGHT)) ||
(!keys.get(Keys.LEFT) && !(keys.get(Keys.RIGHT)))) {
bob.setState(State.IDLE);
// acceleration is 0 on the x
bob.getAcceleration().x = 0;
// horizontal speed is 0
bob.getVelocity().x = 0;
#11 – #13 – 为Bob执行的动作定义一个枚举。每一次按键/触摸都会触发一个动作。
#15 – 定义游戏中世界。我们将控制世界里面存在的实体。
#16 – 定义Bob作为私有成员,它只是世界里面的Bob的一个引用,但是我们需要它,因为每次引用它比在需要它的时候每次都获取要容易。
#18 – #24 – 它是一个静态的储存了键和状态的哈希映射。如果键被按下,则为true,否则为false。它被静态的初始化了。这个映射会用来在控制器的update方法中计算如何处理Bob。
#26 – 这是使用World 作为参数的构造器,同时也得到了Bob的引用。
#33 – #63 – 这些方法只是简单的进行回调,在动作按钮被按下或者在指定的区域触控。这些方法在我们进行任何输入的时候都会被调用。它们简单地设置映射内对应的按键按下的值。正如你所看到的,控制器也是一个状态机,它的状态是由按键映射决定的。
#66 – #69 –在主循环每次调用时都会调用的更新方法。目前它做了2件事情:1-处理输入,2-更新Bob。Bob有一个专门的更新方法,后面我们会看到。
#72 – #92 –
ProcessInput方法轮询键映射的键,并由此来设置Bob的值。例如#73 – #78行,检查向左运动的按键被按下,如果是这样,设置Bob面朝左边、状态为State.WALKING但它的速度是负值。负值是因为在画面上,左为负方向(原点在左下方并且指向右侧)。
向右也是一样。有一些额外的检查,如果这两个键被按下或都没有按下,在此情况下,Bob变成State.IDLE状态,它的水平速度为0。
让我们看看Bob.java中改变的部分。
public static final float SPEED = 4f;
// unit per second
public void setState(State newState) {
this.state = newS
public void update(float delta) {
position.add(velocity.cpy().scl(delta));
只是把SPEED常量改为4单位(块)/秒。
还添加了setState方法,因为我之前忘了。
最有意思的是新获得的update(float delta),它在WorldController中被调用。本方法简单的根据Bob的速度更新它的位置。简单起见,我们只做了这么多而没有检查它的状态,因为控制器负责根据Bob的方向和状态设定它的速度。在这里我们使用了向量数学,libgdx起了很大作用。
我们简单的把Bob的当前位置加上delta秒内移动的距离。我们使用velocity.tmp(),因为tmp()方法创建了一个与速度对象具有相同值的新对象,我们把这个对象的值与所经过的时间delta相乘。在java中我们必须小心的使用引用,因为速度和位置都是Vector2对象。关于向量的更多的信息请访问。
我们几乎做了所有的事情,我们只需要在事件发生的时候调用正确。 libgdx有一个输入处理器,它有几个回调方法。因为我们在使用GameScreen 作为游戏界面,它自然就成为了输入处理器。要做到这一点,GameScreen中将实现InputProcessor。
新的GameScreen.java
package net.obviam.starassault.
import net.obviam.starassault.controller.WorldC
import net.obviam.starassault.model.W
import net.obviam.starassault.view.WorldR
import com.badlogic.gdx.G
import com.badlogic.gdx.Input.K
import com.badlogic.gdx.InputP
import com.badlogic.gdx.S
import com.badlogic.gdx.graphics.GL10;
public class GameScreen implements Screen, InputProcessor {
private WorldR
private WorldC
private int width,
public void show() {
world = new World();
renderer = new WorldRenderer(world, false);
controller = new WorldController(world);
Gdx.input.setInputProcessor(this);
public void render(float delta) {
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
controller.update(delta);
renderer.render();
public void resize(int width, int height) {
renderer.setSize(width, height);
this.width =
this.height =
public void hide() {
Gdx.input.setInputProcessor(null);
public void pause() {
// TODO Auto-generated method stub
public void resume() {
// TODO Auto-generated method stub
public void dispose() {
Gdx.input.setInputProcessor(null);
// * InputProcessor methods ***************************//
public boolean keyDown(int keycode) {
if (keycode == Keys.LEFT)
controller.leftPressed();
if (keycode == Keys.RIGHT)
controller.rightPressed();
if (keycode == Keys.Z)
controller.jumpPressed();
if (keycode == Keys.X)
controller.firePressed();
public boolean keyUp(int keycode) {
if (keycode == Keys.LEFT)
controller.leftReleased();
if (keycode == Keys.RIGHT)
controller.rightReleased();
if (keycode == Keys.Z)
controller.jumpReleased();
if (keycode == Keys.X)
controller.fireReleased();
public boolean keyTyped(char character) {
// TODO Auto-generated method stub
public boolean touchDown(int x, int y, int pointer, int button) {
if (x & width / 2 && y & height / 2) {
controller.leftPressed();
if (x & width / 2 && y & height / 2) {
controller.rightPressed();
public boolean touchUp(int x, int y, int pointer, int button) {
if (x & width / 2 && y & height / 2) {
controller.leftReleased();
if (x & width / 2 && y & height / 2) {
controller.rightReleased();
public boolean touchDragged(int x, int y, int pointer) {
// TODO Auto-generated method stub
public boolean mouseMoved(int x, int y) {
// TODO Auto-generated method stub
public boolean scrolled(int amount) {
// TODO Auto-generated method stub
变化如下:
#13 – 该类实现了InputProcessor。
#19 – Android触摸事件使用的宽度和高度。
#25 – 利用world实例化WorldController。
#26 – 设置当前画面作为应用程序的当前输入处理器。libgdx 把输入处理器当作全局的,所以如果不共享的话每个画面都需要单独设定。在这种情况下,画面本身处理输入。
#47 & #62 – 我们设置全局的输入处理器为null只是为了清理。
#68 – 每当物理键盘上的键被按下时都会触发keyDown(int keycode)方法。参数keycode就是按键的值,在这种方式下,我们可以查询它,如果是我们期望的键就做一些操作。这正是事情的原理。基于我们想要的键,我们把事件传递给控制器。该方法也返回true,让输入器知道输入已经被处理。
#81 – keyUp 与keyDown 方法完全相反,当按键被释放时,它只是委托给WorldController。
#111 – #118 – 这里就让它变得有趣了。它只在触摸屏上发生,坐标和按钮同时被传入。指针是多点触控,代表了它捕获的触摸事件的ID。
这些控制都非常简单,只是为了简单的演示之用。屏幕被分为4部分,如果点触左下象限,那么会被当作触发左移的操作,并且把与桌面版相同的事件传递给控制器。
对于touchUp也是完全一样的东西。
警告: -这是非常错误并且不可靠的,因为没有实现touchDragged方法,当手机划过的时候会搞乱所有的事情。这当然会被修复,其目的是为了演示多个硬件输入和如果把它们关联在一起。
在桌面版和Android上运行程序都会演示控制。在桌面版上使用方向键,在Android上触摸屏幕下部角落将移动Bob。
在桌面版上,你会发现使用鼠标来模拟触控也可以用。这是因为touchXXX 也处理桌面上的鼠标输入。为了解决这个问题,在touchDown和touchUp方法的开始处添加下面的代码。
if (!Gdx.app.getType().equals(ApplicationType.Android))
如果应用程序不是Android,程序返回false并且不执行方法其余部分的代码。请记住,false意味着输入没有被处理。
正如我们看到的,Bob移动了。
到目前为止,我们介绍了不少游戏开发的东西,并且已经做出来一个可以展示的东西。
我们逐渐为我们的应用引入了工作元素,并且一步一步的做了些东西。
我们仍需要添加:
* 地形的交互(块碰撞,跳跃)
* 跟随Bob的镜头
* 敌人和攻击敌人的武器
* 控制的定义和微调
* 游戏结束和开始的更多画面
* libgdx更有趣东西
本项目的源码可以在这里找到:
分支是 part1。
用git检出:
git clone -b part1 :obviam/star-assault.git
你也可以下载一个。
你也可以进入这个系列的,在里面我为Bob加入了动画。
关于作者:
新浪微博:
微信关注: iProgrammer
最热门的技术类微信公共账号之一,全文推送精选技术文章。扫描加关注,碎片时间学习新技能!
Worktile – 团队协作利器
这篇博文 没法读啊 满大篇 ?????????????????????????
Android频道作者
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线博客团队正试图以我们微薄的力量,把优秀的原创/译文分享给读者,为“快餐”添加一些“营养”元素。
伯乐在线-博客(
)专注于分享职业相关的博客文章、业界资讯和职业相关的优秀工具和资源。博文类别包括:程序员、设计、营销、互联网、IT技术、自由职业、创业、运营、管理、翻译和人力资源等等。期待您通过和关注我们。如果您也愿意分享一份自己的原创/译文,可以~
网站合作和广告投放
联系邮箱:Webmaster (at)
(加好友请注明来意)
交换友情链接要求:PR>=4
网站使用问题
请直接询问或者反馈
欢迎关注并订阅伯乐在线博客

我要回帖

更多关于 安卓手机网游修改器 的文章

 

随机推荐