从飞信裸辞已经2个月了,因为对游戏的爱,和做出好玩的游戏这个梦想。《Windows游戏编程大师技巧》(以下简称《大师》)这书已经读完,DEMO也都搞清楚了,为了确实的掌握2D游戏的技术,决定完成一个完整的KOK1咒术师打钱坑的DEMO,也是作为我踏入游戏开发行业的简历附加DEMO。【广告】对游戏有爱的游戏公司(北京)缺人(2D/3D游戏程序员)的话请随时把我拉走。这个系列文章将记载在这个DEMO开发过程中的一点一滴,废话不说了,进入正题。

0. 本期功能

(1)人物可以在地图上通过鼠标右键控制走动

(2)地图可以卷动,并且人物不会超过边界

(3)人物在站立和跑动时会有相应的动画

(4)人物在站立和跑动时会面向适当的方向

(5)鼠标有动画,并且无法移出窗口

截图:

可直接运行版本 >>下载页地址<<

1. 开发工具与语言

在学习《大师》的时候,他主要是用C实现的,并且在性能敏感的位置穿插了一些汇编代码,我的示例最开始也是用C来写,并且贯彻作者多使用全局变量的做法,但当逻辑逐渐复杂了之后,太多的函数与全局变量使得开发效率受到了不小的影响,所以这个DEMO我使用C/C++来写,主要的对象按类来封装,对于性能方面没有过多的注意,以后再重构和优化吧。本文的开发环境是VS2010,DirectX 8.1 SDK,图形部分使用DirectDraw7。

2. 主要对象

目前的DEMO主要有以下几个对象:

GameScreen - 游戏屏幕,就是用户窗口的客户区,这个类提供游戏屏幕相关的属性,如宽高、左上角在世界地图的坐标、以及一个视频控制器。

Map - 游戏地图,这个类提供了地图相关的属性,如每个小图块的宽高,地图数据数组,横向和纵向有多少块小图块。还有地图的初始化(Init)、每一帧绘制(Draw)、释放(Release)的方法。

Player - 玩家,这个类提供了玩家相关的属性,如玩家位图的宽高,在屏幕绘制的左上角坐标,在世界地图的坐标,跑动速度,目标坐标等等,同时也提供了初始化(Init)、每一帧绘制(Draw)、释放(Release)的方法。

Bitmap24 - 24位位图,这个类提供了对24位位图的读取功能,把字节顺序调整为正序,并且改为32BPP的格式。详细的实现方法可见我前面的两篇文章:32BPP窗口模式下24位位图的像素操作(1) 、32BPP窗口模式下24位位图的像素操作(2) 。

VideoManger - 视频管理器,这个类封装了DirectDraw7的相关操作,如创建表面、裁剪表面、从位图中取元素等。

MouseManager - 鼠标管理器,这个类封装了DirectInput的相关操作,以获取最新的鼠标情况,详细的实现方式在前面的这篇文章里:使用dinput鼠标的相对模式达到绝对定位。

Controller - 操作管理器,这个类分离了用户的操作与游戏里的逻辑。Controller::ProcessRequest()将在每帧调用,用来处理用户操作,并调用这个管理器封装的相关逻辑操作,如PlayerMove()。

3. 全局流程

(1)流程很简单,首先就是创建窗口,并且调整好窗口的大小和位置,为什么还要调整呢,原因和解决方案在这篇文章里:确定窗口实际用户区的一些问题。

(2)就是在窗口创建好了之后,在主消息循环开始之前,进行我们所需要的对象的初始化工作。

gameScreen.Init(SCREEN_WIDTH, SCREEN_HEIGHT, hWnd, titleSize, borderSize, 100, 100); gameMap.Init(&gameScreen); player.Init(&gameScreen); mouse.Init(&gameScreen);

(3)然后开始主消息循环,这里要注意使用PeekMessage,并且Peek后REMOVE掉,否则将不是一个真正实时的游戏循环。在主消息循环里执行我们的游戏逻辑。

gameScreen.Video.FillSurface(gameScreen.Video.Lpddsoffscreen, 0); mouse.RefreshData(&gameScreen); controller.ProcessRequest(&gameScreen, &gameMap, &player, &mouse); gameMap.Draw(&gameScreen); player.Draw(&gameScreen); mouse.Draw(&gameScreen); gameScreen.Video.FlipSurface(); Sleep(100);

(4)最后在主循环退出后释放所有资源。

gameScreen.Release(); gameMap.Release(); player.Release(); mouse.Release();

4. 角色动画的实现

鼠标动画的绘制非常简单,请下载一看源码就知道了,地图的实现方法就是在一个大表面上来取一块放在显存里,也很简单,这里都不再赘述了。

角色动画涉及8个方向和两个状态(站立和跑动)的动画。所以我们要准备好(8*站立动画帧数 + 8*跑动动画帧数)个表面,并且有两个动画索引数组。准备好后,我们可以考虑到实现角色动画无法就是判断在当前游戏帧,我们应该把上面那么多个表面其中的哪一个写到显存里去。因素有3:

(1)Direction - 人物朝向

(2)AnimationIndex - 人物动画当前索引

(3)Status - 人物状态是在站立还是跑动

首先分析人物朝向。人物朝向是受玩家给出的目标移动指令后才做改变的,所以可以考虑在Controller::PlayerMove()执行时来修改这个值。

int Controller::PlayerMove(GameScreen *gs, Map *map, Player *player, MouseManager *mouse) { if (player->MapX == player->TargetX && player->MapY == player->TargetY) { player->Status = 0; return 1; } // 调整player->Direction if (player->MapX == player->TargetX && player->MapY < player->TargetY) player->Direction = 0; else if (player->MapX > player->TargetX && player->MapY < player->TargetY) player->Direction = 1; else if (player->MapX > player->TargetX && player->MapY == player->TargetY) player->Direction = 2; else if (player->MapX > player->TargetX && player->MapY > player->TargetY) player->Direction = 3; else if (player->MapX == player->TargetX && player->MapY > player->TargetY) player->Direction = 4; else if (player->MapX < player->TargetX && player->MapY > player->TargetY) player->Direction = 5; else if (player->MapX < player->TargetX && player->MapY == player->TargetY) player->Direction = 6; else if (player->MapX < player->TargetX && player->MapY < player->TargetY) player->Direction = 7; // 调整gs->MapX(Y) if (player->MapX < player->TargetX) { if (player->TargetX - player->MapX <= player->RunSpeed) player->MapX = player->TargetX; else player->MapX += player->RunSpeed; } if (player->MapX > player->TargetX) { if (player->MapX - player->TargetX <= player->RunSpeed) player->MapX = player->TargetX; else player->MapX -= player->RunSpeed; } if (player->MapY < player->TargetY) { if (player->TargetY - player->MapY <= player->RunSpeed) player->MapY = player->TargetY; else player->MapY += player->RunSpeed; } if (player->MapY > player->TargetY) { if (player->MapY - player->TargetY <= player->RunSpeed) player->MapY = player->TargetY; else player->MapY -= player->RunSpeed; } gs->MapX = player->MapX - gs->ScreenWidth / 2; gs->MapY = player->MapY - gs->ScreenHeight / 2; return 1; }

接着是人物动画索引。这个可以在人物绘制时处理,在每次绘制完成后,将这个索引加1即可。

最后是Status。这个更简单了,必然是在用户做出操作后来修改。

int Controller::ProcessRequest(GameScreen *gs, Map *map, Player *player, MouseManager *mouse) { if (mouse->RButton) { player->Status = 1; player->TargetX = gs->MapX + mouse->X; player->TargetY = gs->MapY + mouse->Y; if (player->TargetX > map->TileWidth * map->MapWidth - gs->ScreenWidth / 2 - 1 - player->RunSpeed) player->TargetX = map->TileWidth * map->MapWidth - gs->ScreenWidth / 2 - 1 - player->RunSpeed; if (player->TargetX < gs->ScreenWidth / 2 - 1 + player->RunSpeed) player->TargetX = gs->ScreenWidth / 2 - 1 + player->RunSpeed; if (player->TargetY > map->TileHeight * map->MapHeight - gs->ScreenHeight / 2 - 1 - player->RunSpeed) player->TargetY = map->TileHeight * map->MapHeight - gs->ScreenHeight / 2 - 1 - player->RunSpeed; if (player->TargetY < gs->ScreenHeight / 2 - 1 + player->RunSpeed) player->TargetY = gs->ScreenHeight / 2 - 1 + player->RunSpeed; } if (player->Status == 1) { PlayerMove(gs, map, player, mouse); } return 1; }

因为以上3因素已经都有归属了,所以在每一帧,我们只要根据3因素画出相应的表面即可 。

int Player::Draw(GameScreen *gs) { RECT dest_rect = { DrawX, DrawY, DrawX + Width, DrawY + Height }; LPDIRECTDRAWSURFACE7 drawSurface; if (Status == 1) { if (AnimationIndex >= RunAnimationDataCount) AnimationIndex = 0; drawSurface = RunSurfaces[RunAnimationData[AnimationIndex] + Direction*RunAnimationFrameCount]; } else { if (AnimationIndex >= StandAnimationDataCount) AnimationIndex = 0; drawSurface = StandSurfaces[StandAnimationData[AnimationIndex] + Direction*StandAnimationFrameCount]; } ++AnimationIndex; gs->Video.Lpddsoffscreen->Blt(&dest_rect, drawSurface, NULL, DDBLT_WAIT | DDBLT_KEYSRC, NULL); return 1; }

5. 总结

至此,本期的功能都实现,但还有以下主要不足:

(1)目前角色的移动是将当前角色坐标和目标坐标做比对,然后在X和Y上都加上RunSpeed,而实际应该是求出向量,在向量上增加RunSpeed。这样角色移动的速度就更符合逻辑了。

(2)因为这个DEMO的图像都是我从游戏里截图出来的(PS了很久啊~~),所以帧数相当低。

6. 源码下载

>>下载地址页<< 资源分是1,有兴趣和诚意的朋友下吧。

7.下一期将完成除了现在地表图像的绘制,还要在地图上绘制可以阻挡住人物的护栏、石头等物体,以及人物可以自动绕过障碍的寻路逻辑。

从零开始重写KOK1(万王之王1) —— (1)让人物可在地图上使用鼠标跑动相关推荐

  1. 从零开始重写KOK1(万王之王1) —— (2)优化地图加载

    本来想在第2篇说明物体遮挡与寻路的开发过程,但是因为我把这问题想简单了,现在已经完成了遮挡与寻路,但是中间的过程非常多,第一篇文章的系统结构需要做一些修改才可以,这里先说一下地图加载的相关问题. 首先 ...

  2. 从零开始重写KOK1(万王之王1) —— (3)优化玩家移动与精确8方向朝向

    0. 回顾与分析 在第1篇文章中,我们是对移动目标与玩家坐标做差,然后按照各个方向的可能性进行if判断,来决定人物的方向,在第一篇的DEMO中,可以发现,只有当人物正好在正斜方(45度的4个斜方向,或 ...

  3. 万王之王3D手游怎么在电脑上玩?万王之王3D安卓模拟器使用详细教程

    万王之王3D是腾讯发型的一款魔幻题材的MMORPG游戏,已经于近期全平台上线了.那么怎么在电脑上玩万王之王3D手游呢?其实想用鼠标键盘大屏玩万王之王3D手游非常简单,不需要安卓模拟器,也不需要你有高配 ...

  4. 《万王之王3》主题歌歌词有奖大征集

    <万王之王3>主题歌歌词有奖大征集 http://ztg.kok3.ztgame.com/

  5. 龙之谷微信该服务器已爆满,万王之王3D服务器已爆满怎么办 爆满区服如何进入...

    在万王之王3D中,开测后大多数服务器人都比较多,有些服务器甚至出现了爆满的情况,遇到服务器爆满应该如何解决呢? 1.选择其他服务器 游戏中有些服务器比较火爆,人数比较多,所以就会爆满,大家进不去这个游 ...

  6. 万王之王3d服务器正在维护,万王之王3D服务器尚未对外开放怎么回事_万王之王3D服务器进不去解决方法_玩游戏网...

    <万王之王3D>安卓和ios能一起玩吗 腾讯有一款新游戏万王之王3D手游上架了,你又可以选择自己喜欢的职业进行冒险之旅了.当然任何游戏都喜欢,也是大家必须询问了一个关键性问题,那就是万王之 ...

  7. 万王之王 列王记 ikok 全职业挂机代码

    原生为zkok代码,后由 小混混 帮忙转为ikok代码 自动判断职业调用对应职业的挂机代码(多个相同职业需要按照角色名调整代码) 以法王坐标的中心,其他角色设置相对坐标 %public_bc rebo ...

  8. 电机驱动芯片界的“卷”王之王-----Trinamic!

    要说卷,每分钟转xx圈,谁还能有它"卷"?! 要说卷, "承包"工业能耗30%, 双碳压力下,谁能有它非卷不可的压力?! 要说卷,工业.汽车.航天.医疗--生活 ...

  9. 微支付 js-api java 坑王之王!!!

    微支付 js-api java 坑 转载请指明出处,版权必究.此文章是我们在经历了各种坑之后的结晶,请爱惜她. 最后更细时间:2015年5月4日 前言: 微支付的js-api,我是不想要喷你,是全国人 ...

最新文章

  1. Pytorch: 命名实体识别: BertForTokenClassification/pytorch-crf
  2. mysql查询指定日期
  3. java process 重定向_java – 没有重定向StdOut的Processbuilder
  4. OVS DPDK vhost-user详解(十三)
  5. 已解决:PC插上串口工具后PC端口com那里有个黄色叹号,无法使用串口工具
  6. 如何让VB6代码编辑器垂直滚动条随鼠标滚轮滚动
  7. vue里面怎么删除部分页面_基于VUE选择上传图片并页面显示(图片可删除)
  8. javascript OOP 面向对象编程
  9. 【翻译】Scott Mitchell的ASP.NET2.0数据指南中文版索引
  10. 获取XML的文件信息
  11. u2020 华为_华为MateBook X Pro 2020款评测:全面屏商务旗舰再升级
  12. 电力行业信息安全等级保护管理办法_信息安全等级保护是什么???
  13. jsp action java_jsp中Action使用session方法实例分析
  14. Hadoop学习笔记—6.Hadoop Eclipse插件的使用
  15. Linux 设备树的解释 - DTB文件格式【转】
  16. OpenCasCade图元拖动问题
  17. 雅马哈机器人左手右手系统_YAMAHA机械手操作手册.PDF
  18. 兔子能不能跑得过乌龟
  19. 解决微信公众平台图片不可引用
  20. 91pay.php,电商网站使用支付宝转账付款功能代替支付宝支付接口

热门文章

  1. Lumentum22045498激光器
  2. web前端入门到实战:SCSS 常用属性合集
  3. Python爬虫,自动下载cosplay小姐姐图片!
  4. 考研数学|23强化分题型习题数二【目录】
  5. 小米10开始抓取日志怎么关闭_小米10手机屏幕是从三星换成了国产华星光电,会降价吗...
  6. tplink703n变无线打印服务器,TPLink wr703n 无线小路由刷OPENWRT 不需要拆机 4M
  7. 世上最健康的作息时间表 七点半起床十一点半睡觉
  8. hd630支持分辨率_UHD630相当于什么显卡?HD630和UHD630核显区别大吗?
  9. iOS- P12发生错误,无法导入项目
  10. Vue(五)——调试