|
对于这个场景你是不是很熟悉?
对于找游戏时你是不是这种感觉?
对于玩游戏你是不是有这种感觉:
氪金时你是不是这种感觉:
没错,这不是毒鸡汤,这是一个赤裸裸的现实:
你能怎么办,退坑保平安还是无能狂怒.还是当个老老实实的吃瓜群众?
归根结底,别人的游戏终归会让你憋屈,玩得多了,也会有人说你有网瘾需要电电才老实.所以说少整些那么多有的没的.
那么有没有一款游戏,玩起来不憋屈,还能学到东西,别人不仅不会说你玩物丧志,还会说你牛逼?当然有!那款游戏叫自己做的游戏.
你想想,还有啥游戏,比自己做游戏好玩,规则自己定,不怕猪队友,为所欲为有木有
啥,需要团队,美工,音效,程序猿.资金链?不需要的,下面就由本老司机教各位如何用2小时(划掉) 两天 撸出一款游戏然后爽两个月.
那么小葵花课堂开课啦,不会码不会画只会玩怎么办,多半是废了,电一电就好了(划掉)
没关系,我们的口号是,小学生灵魂画师的水平,中等程度的码农技能点,你就能玩一个叫"我自己做的游戏"的游戏了
准备工具 硬件部分:ipad 和pencil,也可以用数位屏,数位板,电脑:
准备工具 软件部分(Visual Studio 2010,PainterEngine):
Visual Studio是宇宙最强IDE(不接受反驳),笔者用的是Visual Studio 2010版本,简单来讲就是编程开发用的,不过用啥玩意编程并不绝对重要,你可以换用其它版本的Visual Studio或者Visual Studio code,Qt,DEV C++,codeblocks,C Free都没问题。
而关于PainterEngine说明一下,PainterEngine 是一个高度可移植完整开源的图像渲染器,或者说PainterEngine是一个游戏引擎,你可以在这个回答中找到我之前写的PainterEngine的教程
https://www.zhihu.com/question/35391145/answer/718766924
很多人认为PainterEngine就是个简单的UI绘图库,实际上并不是这样。等会将会演示PainterEngine在游戏的快速开发中是如何运用的,而这也是PainterEngine这个引擎设计的最终目的。
在这里下载PainterEngine:
matrixcascade/PainterEngine别忘了点个Star:
目标:两天撸一款游戏
游戏类型:3A大作没钱,贪吃蛇,连连看太low,2小时的时间也不多,那做个什么游戏好呢,显然你要问我,答案就只有灰机大战了.
开发语言:C语言,说到这里,估计评论区又有一堆杠精跳出来说,哎呀,什么年代了还用C语言做游戏,为什么不用Unity啊,为什么不用那么多知名游戏引擎啊,多高大上还简单,C语言不适合做游戏,效率太低,要用C#,JS,引擎要用C++,面向对象支持泛型balabala…,对于这些笔者混迹码农界十五六余年什么样的杠精没见过,也懒得多费什么口水讲道理,简单总结一点就是这么干我乐意,你喜欢你自己用,反正我不用.
当然,本文技术部分面向有一定编程基础及有一定美术基础的看官们,如果对这两方面不是很熟悉的看官可以权当做看个乐子,把成品下载下来爽爽就OK,动手能力强的看官可以对照本篇文章琢磨琢磨,这都欢迎。
起名
既然是做游戏,2小时的时间恐怕也不用写什么策划文案了,第一步当然是给游戏起个响亮的名字,幸好起名字不用啥时间,而且整个游戏项目就自己一个人,也省了讨论的麻烦,既然是灰机大战,那突出主题,明确重点,既要高大上,又要和时代接轨,最后带点中国特色 索性就叫space old driver好了(当然我知道这是chinese english,这点就别杠了)
首先,创建PainterEngine项目,关于创建项目这部分,你可以在
https://www.zhihu.com/question/35391145/answer/718766924
找到详细的教程,这里我再重新复述一遍
安装PainterEngine
PainterEngine无需额外的安装步骤,你只需要从GitHub或其它渠道下载PainterEngine的代码就可以了。
1. 访问网址:https://github.com/matrixcascade/PainterEngine
2. 点击Clone or download---->Download Zip.
3. 解压下载的压缩包
4. 打开解压压缩包,再解压压缩包中的PainterEngine Tools
完成后你会得到这样的文件夹
使用PainterEngine
以Visual Studio 2010为例:
第一步:打开Visual Studio
第二步,点击新建项目
第三步,点击visual C++,选择一个空项目,然后随便填一个名字后点击确定
*第四步,右键点击项目,选择添加,新建筛选器,然后把筛选器取名为PainterEngine,这步只是建议,可以选择不做.
第五步,将PainterEngine复制到这个项目的目录下,右键点击项目.点击在windows资源管理器中打开文件夹.
第七步,把PainterEngine这个目录拖动到Visual Studio项目中(如果有做第四部就把他拖到筛选器中)
第八步,右键源代码-->添加--->新建项
第九步:创建main.c(或者别的什么名字.c)你可以开始写代码了
创建运行时环境代码
因为PainterEngine的内存管理由独立于操作系统的内存池接管,在创建游戏代码之前,先对游戏运行时资源进行一个初步的规划
游戏运行时总共的内存:64M
当中包括了:
用于UI的内存:2M
用于加载资源内存:32M
用于游戏逻辑运算的内存:16M
800x600的主要渲染表面:2M
临时计算缓存:8M
剩余的4M内存,预留用于,内存池内部的节点分配,碎片消耗等.
同时,游戏需要具备控制台,音效播放,字模加载等额外功能,这也就意味着游戏需要用到混音算法和字模库
为此,笔者创建了一个SOD_Runtime.h和SOD_Runtime.c用于管理这些库
/*SOD_Runtime.h*/
#ifndef SOD_RUNTIME_H
#define SOD_RUNTIME_H
#define SOD_RUNTIME_WINDOW_WIDTH 800 //游戏窗体宽度
#define SOD_RUNTIME_WINDOW_HEIGHT 600//游戏窗体高度
#include "../../PainterEngine/Architecture/PainterEngine_Console.h"
typedef struct
{
PX_Runtime runtime;//PainterEngine运行时环境
PX_SoundPlay sound;//混音器
PX_Console Console;//控制台
PX_FontModule fontmodule;//字模库
}SOD_Runtime;
px_bool SOD_RuntimeInitialize(SOD_Runtime *runtime);//游戏运行时初始化代码
#endif
/*SOD_Runtime.c*/
px_byte SOD_RUNTIME_MEMORY[1024*1024*64];//游戏运行时内存空间
#define SOD_RUNTIME_UI_SIZE 1024*1024*2 //用于UI的内存
#define SOD_RUNTIME_RESOURCES_SIZE 1024*1024*48//用于资源的内存
#define SOD_RUNTIME_GAME_SIZE 1024*1024*12//用于游戏逻辑运算的内存
px_bool SOD_RuntimeInitialize(SOD_Runtime *sodruntime)
{
PX_IO_Data data;
//初始化游戏运行时代码
if (!PX_RuntimeInitialize(&sodruntime->runtime,SOD_RUNTIME_WINDOW_WIDTH,SOD_RUNTIME_WINDOW_HEIGHT,SOD_RUNTIME_MEMORY,sizeof(SOD_RUNTIME_MEMORY),SOD_RUNTIME_UI_SIZE,SOD_RUNTIME_RESOURCES_SIZE,SOD_RUNTIME_GAME_SIZE))
{
return PX_FALSE;
}
//初始化控制台
if (!PX_ConsoleInitialize(&sodruntime->runtime,&sodruntime->Console))
{
return PX_FALSE;
}
//初始化混音器
if(!PX_SoundInit(&sodruntime->runtime.mp_game,&sodruntime->sound))
{
return PX_FALSE;
}
//读取字模文件
data=PX_LoadFileToIOData(SOD_FONTMODULE_PATH);
if (data.size==0)
{
return PX_FALSE;
}
//加载字模文件
if (!PX_FontModuleLoad(&sodruntime->fontmodule,data.buffer,data.size))
{
return PX_FALSE;
}
return;
}创建游戏调度器代码
游戏调度器主要的目的是游戏在不同的功能间切换,在本游戏中,用到的界面主要有三个,一个是进入游戏时的启动界面,一个是游戏界面,另一个是加载replay的界面(就是简单的一个加载文件对话框)
为此,笔者创建了一个SOD_Game.h和SOD_Game.c来统一管理游戏的内存和逻辑界面调度关系:
先来看看文件SOD_Game.h
#ifndef SOD_GAME_H
#define SOD_GAME_H
#include "SOD_Runtime.h"
typedef struct
{
//这部分的代码待完善
SOD_Runtime sodrt;
}SOD_Game;//调度器结构体
px_bool SOD_GameInitialize(SOD_Game *game);//游戏初始化,其中游戏运行时的代码也由调度器负责调用初始化
px_void SOD_GamePostEvent(SOD_Game *game,PX_Object_Event e);//事件处理,用于接收IO设备比如键盘鼠标触摸屏游戏杆的事件,将事件分发到合适的界面中
px_void SOD_GameUpdate(SOD_Game *game,px_dword elpased);//游戏循环更新
px_void SOD_GameRender(SOD_Game *game,px_dword elpased);//游戏渲染
#endif然后是SOD_Game.c
#include "SOD_Game.h"
px_bool SOD_GameInitialize(SOD_Game *game)
{
if (!SOD_RuntimeInitialize(&game->sodrt))//初始化游戏运行时
{
return PX_FALSE;
}
return PX_TRUE;
}
px_void SOD_GamePostEvent(SOD_Game *game,PX_Object_Event e)
{
PX_ConsolePostEvent(&game->sodrt.Console,e);//将IO事件派发给控制台
}
px_void SOD_GameUpdate(SOD_Game *game,px_dword elpased)
{
PX_ConsoleUpdate(&game->sodrt.Console,elpased);//更新游戏控制台
}
px_void SOD_GameRender(SOD_Game *game,px_dword elpased)
{
PX_SurfaceClear(&game->sodrt.runtime.RenderSurface,0,0,game->sodrt.runtime.RenderSurface.width-1,game->sodrt.runtime.RenderSurface.height-1,PX_COLOR(255,255,255,255));//清理渲染表面
PX_ConsoleRender(&game->sodrt.Console,elpased);//渲染控制台界面
}完成上述代码后,游戏的最基本的初始化工作就算完成了具体的实现请看SOD_Runtime.c文件,因为windows的主函数文件main.c是之前就写好的模板,虽然代码较多但改动其实一两分钟就完成了,这里就不贴出来了,main.c主要内容是读取键盘设备的信息,将混音器的PCM数据写到系统缓存中播放,最后将PainterEngine渲染后的图形缓存显示出来,这里就不贴出来的,游戏代码完全开源,有兴趣可以打开看看.
运行界面试试:
启动界面
启动界面就是指游戏程序打开后用户第一个看到的界面,首先鉴于时间关系,就随便涂鸦一个背景就好:
启动后一共只需要三个按钮,开始游戏,回放,退出游戏,为了支持中文字模,使用PainterEngine tools里的字模生成工具,字体使用不需要商业授权的幼圆.ttf,编码常用asc码英文字符和几个用得到的汉字
内容如下
~!@#$%^&*()_+`1234567890-
=qwertyuiop[]\asdfghjkl;&#39;zxcvbnm,./QWERTYUIOP{}|ASDFGHJKL:&#34;ZXCVBNM<>?开始游戏退出回放分数将上述文本以txt文件保存,编码为UFT16-LittleEndia
生成sod.pfx字模文件,然后使用PainterEngine加载该字模最后绘制在界面上
其中,SOD_StartUp.h及SOD_Startup.c代码用于渲染及执行启动界面的逻辑.你可以在源码中查看它详细的实现细节.
运行截图:
游戏界面
点击开始游戏后,进入的就是游戏界面了,为此,笔者新建了2个文件分别是SOD_Play.h SOD_Play.c,游戏的运行代码就在这两个文件内完成
创建世界和飞船
使用PX_WorldInit创建一个世界,这个函数在SOD_PlayReset(SOD_Play *pPlay)函数中被调用,在每次点击开始游戏时,都会调用SOD_PlayReset函数,创建世界后,默认会开启辅助线绘制,在“星际老司机”这款游戏中,默认创建一个长宽为1600*1200的世界
同时,用一个结构体来描述飞船的基本属性
typedef struct
{
px_bool show;//是否显示飞船
px_int life;//飞船的声明
px_texture *shipObject;//纹理对象
px_point velocity;//加速度
px_point force;//推力
px_point direction;//方向
px_int max_life;//最大生命
px_float max_force;//最大受力
px_float max_speed;//最大速度
px_bool showHelpLine;//辅助线
}SOD_Play_Object_Ship; 在px_void PX_Object_ShipUpdate(PX_Object *pObject,px_uint elpased)函数中,通过对飞船的参数物理描述,对飞船的物理信息进行更新,
在px_void PX_Object_ShipRender(px_surface *psurface, PX_Object *pObject,px_uint elpased)函数中,对飞船在世界中进行绘制,详细的代码细节你可以在SOD_Play.c中找到
运行界面如图
创建飞船的动力系统及约束
现在需要解决的一个问题是,如何操控飞船,这直接决定了这款游戏的玩法,为此,笔者设计了一种飞船的操作方式,并将它叫做弹簧操作系统
飞船的推动力方向永远跟随着鼠标方向,并且距离鼠标的距离越远,拉力力度越大.
同时,要注意飞船的飞行范围在地图内,需要不断检查飞船的位置避免它飞出地图外了.
运行截图如下:
进一步完善UI
到这里,飞船的操作基本完成了,现在将飞船的一些基本信息显示出来,一来玩家需要看到一些实际需要的参数让游戏进行下去,二来也能让游戏显得更加高大上.
在px_void SOD_PlayRenderUI(SOD_Play *pPlay)函数中你可以看到UI的具体实现细节
主要包括了几个方面
1. 绘制鼠标的标识
2. 绘制推动力指示环
3. 绘制生命条
4. 当前分数
5. 当前游戏时间
6. 武器栏
运行截图:
喷射粒子
尽管飞船有了基本的动力系统和操作,但是现在仅仅只是一个移动的贴图,缺少视觉上和操控上的感觉,因此,使用粒子系统来实现尾部火焰的喷射效果,
在SOD_Play.h中:
PX_Object *ship_powerpartical_main,*ship_powerpartical_l1,*ship_powerpartical_l2;
用于描述飞船的两个主推动器和两个副推动器的粒子对象,这些对象在SOD_PlayReset()函数中完成初始化,并在SOD_Play.c的SOD_Object_ShipUpdate函数中完成位置与角度的更新.
运行截图如下:
完善武器系统
动力系统完成后,接下来就开始完成武器系统了,因为只是一个小小的DEMO,所以没有搞得太复杂就只做了一种武器(如果你想多加点武器系统可以通过自己修改源代码来加),笔者琢磨了一番,那种打击感强烈,玩起来最舒服的想来想去还是Machine Gun,因此一个machine gun武器系统诞生了,不过,为了避免让游戏变得太没有难度.笔者还是给武器系统加上了一些限制,比如对子弹数量加了一些限制:
第一个敌人:陨石
武器系统就绪后,是时候加上一些最基础的敌人了,作为一个宇宙空间老司机,自然少不了在小行星带里的一堆陨石中玩漂移,为了让陨石的生成更加的真实点,让陨石的大小在一个范围内随机缩放,同时花了两分钟画了3张陨石的纹理贴图,然后让陨石能够有一个角速度进行自转.你可以在SOD_Play.c中找到SOD_Object_Stone前缀的函数,那里描述了陨石的执行逻辑关系.
第二个敌人 幽灵:
加入陨石后,第二种敌人也如法炮制(无非就把贴图改一下),不过为了让游戏更有意思一点,我们让第二种敌人当玩家在它附近的时候,他会毫不犹豫的黏上去,当然,第二个敌人使用了PainterEngine中的2dx动画集,让它看起来更有趣一点,你可以在SOD_Play.c中关于SOD_Object_Ghost或带有Ghost的函数中看到这个幽灵的实现细节:
第三个敌人:外星人
这个就厉害了,星系的外星人是暴躁老哥一族的,当你进入它的射程范围内时,它会毫不犹豫向你发射电浆炮,同时如果你的炮弹击中了它,它会马上启动超级暴怒模式,开着飞船就向着你撞过来
升级系统
即使是作为一个开发时间不到两天的DEMO小游戏,升级系统仍然是一个不能漏掉的东西.毕竟打怪不能升级多没意思,作为一个老司机,通过击破陨石幽灵或者外星人有一定的几率会掉落升级星星,拾取后将会对飞船性能有一点提升或者恢复生命补充弹药:
加点音效
最后,给游戏加点音效,但作为一个音痴,不会做音效怎么办.好说,你可以选中加个群,然后去找群里的大佬要点音效,做一个结结实实的白嫖怪
或者你可以用”万能问题解决器” “买!买!买!”
最后,笔者从epic stock media 购买了一堆音效,并将它们应用到了游戏当中
当然,PainterEngine使用的是自带的混音算法,你可以在PX_Sound.h PX_Sound.c中查看他们,关于其他的版权说明,你可以在resources文件夹下的copyrights找到
完成游戏
最后,是时候给这个小小的DEMO游戏收个工了,设计游戏的Game Over界面,上面要显示最终的得分,好让玩了高分的玩家可以截图出来嘚瑟嘚瑟,Replay暂时先不弄了,改成About吧
最后Release游戏(很重要,PainterEngine debug发布有很多调试算法和内存检测算法会拖累游戏性能导致FPS下降,Release发布后就没有这个问题了)
最后,录制游戏先爽爽:
==========================================================
华丽的分割线
==========================================================
如何玩游戏
一: 当然是尽量打高分咯,比如挑战10000分,可惜的是,即使作为作者我也最高只能玩到5000分左右
二: 魔改游戏,想法我都想好了,星际老司机大战滑稽Boss怎么样.
首先找一张滑稽的贴图
然后Photoshop处理一下,将它转换为traw格式加入到游戏资源当中
来到星际老司机的源代码,复制alien和alienfireatom的代码,分别描述的是外星人和它发射的子弹,修改一下
//////////////////////////////////////////////////////////////////////////
//User fire
//子弹碰撞直接删除
px_void SOD_OnUserBulletDamage(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
SOD_Play *pPlay=(SOD_Play *)pObject->User_ptr;
PX_WorldRemoveObject(&pPlay->world,pObject);//删除子弹
SOD_CreateExplosion(pPlay,PX_POINT(pObject->x,pObject->y,0),1);//创建爆炸效果
}
//子弹碰撞
px_void SOD_OnUserBulletImpact(PX_Object *pObj,PX_Object_Event e,px_void *ptr)
{
PX_Object_Event pe;
SOD_Play *pPlay=(SOD_Play *)pObj->User_ptr;
PX_WorldRemoveObject(&pPlay->world,pObj);//删除子弹
SOD_CreateExplosion(pPlay,PX_POINT(pObj->x,pObj->y,0),10);//创建爆炸效果
pe.Event=SOD_OBJECT_EVENT_DAMAGE;//damage消息
pe.Param_int[0]=SOD_USER_BULLET_DAMAGE;//伤害量
PX_ObjectPostEvent((PX_Object *)e.param_ptr[0],pe);//投递消息
}
SOD_Play_Object_UserBullet *SOD_Object_GetUserBullet(PX_Object *pObject)
{
return (SOD_Play_Object_UserBullet *)pObject->pObject;
}
px_void SOD_Object_UserBulletFree(PX_Object *pObject)
{
}
px_void SOD_Object_UserBulletUpdate(PX_Object *pObject,px_dword elpased)
{
SOD_Play *pPlay=(SOD_Play *)pObject->User_ptr;
SOD_Play_Object_UserBullet *pbl=SOD_Object_GetUserBullet(pObject);
pbl->rotation+=pbl->rotationSpeed*elpased/1000;//更新旋转角度
pObject->x+=pbl->velocity.x*elpased/1000;//依据速度更新位置
pObject->y+=pbl->velocity.y*elpased/1000;
//子弹越界(超出地图范围),删除
if (pObject->x<-SOD_STONE_GEN_SPACE-1||pObject->x>pPlay->world.world_width+SOD_STONE_GEN_SPACE+1||\
pObject->y<-SOD_STONE_GEN_SPACE-1||pObject->y>pPlay->world.world_height+SOD_STONE_GEN_SPACE+1\
)
{
PX_WorldRemoveObject(&pPlay->world,pObject);
}
else
{
//生存时间过,删除
if (pbl->alive>elpased)
{
pbl->alive-=elpased;
}
else
{
//删除子弹
PX_WorldRemoveObject(&pPlay->world,pObject);
//创建爆炸效果
SOD_CreateExplosion(pPlay,PX_POINT(pObject->x,pObject->y,0),1);
}
}
}
px_void SOD_Object_UserBulletRender(px_surface *psurface,PX_Object *pObject,px_dword elpased)
{
SOD_Play_Object_UserBullet *pbl=SOD_Object_GetUserBullet(pObject);
//渲染子弹
PX_TextureRenderEx(psurface,pbl->tex,(px_int)pObject->x,(px_int)pObject->y,PX_TEXTURERENDER_REFPOINT_CENTER,PX_NULL,1.0f,pbl->rotation);
}
PX_Object* SOD_Object_UserBulletCreate(SOD_Play *play,px_float x,px_float y,px_float velocity)
{
PX_Object *pObject;
SOD_Play_Object_UserBullet *pbl;
px_point shippt,atom_v;
pbl=(SOD_Play_Object_UserBullet *)MP_Malloc(&play->runtime->runtime.mp_game,sizeof(SOD_Play_Object_UserBullet));
if (pbl==PX_NULL)
{
PX_ASSERT();
return PX_NULL;
}
pObject=(PX_Object *)PX_ObjectCreate(&play->runtime->runtime.mp_game,PX_NULL,0,0,0,0,0,0);
if (!pObject)
{
MP_Free(&play->runtime->runtime.mp_game,pbl);
PX_ASSERT();
return PX_NULL;
}
pObject->x=x;
pObject->y=y;
pObject->z=SOD_ATOM_Z;
pObject->Width=48;
pObject->diameter=48;
pObject->Height=48;
pObject->pObject=pbl;
pObject->Type=SOD_OBJECT_TYPE_USER_ATOM;
pObject->ReceiveEvents=PX_TRUE;
pObject->Func_ObjectFree=SOD_Object_UserBulletFree;//回调
pObject->Func_ObjectUpdate=SOD_Object_UserBulletUpdate;
pObject->Func_ObjectRender=SOD_Object_UserBulletRender;
pObject->User_ptr=play;
pObject->impact_Object_type=SOD_IMPACTTEST_OBJECTTYPE_ENEMY;//碰撞类型
pObject->impact_test_type=SOD_IMPACTTEST_OBJECTTYPE_USER;//碰撞测试类型
shippt.x=play->shipObject->x;
shippt.y=play->shipObject->y;
shippt.z=0;
atom_v=PX_PointSub(shippt,PX_POINT(x,y,0));
pbl->tex=PX_ResourceLibraryGetTexture(&play->runtime->runtime.ResourceLibrary,SOD_KEY_TEX_HUAJI);//绑定纹理
pbl->velocity=PX_PointMul(PX_PointUnit(atom_v),velocity);
pbl->alive=SOD_USER_ATOM_ALIVE_TIME;//生存时间
pbl->rotationSpeed=360;//初始角速度
PX_ObjectRegisterEvent(pObject,PX_OBJECT_EVENT_IMPACT,SOD_OnUserBulletImpact,PX_NULL);//处理碰撞事件
PX_ObjectRegisterEvent(pObject,SOD_OBJECT_EVENT_DAMAGE,SOD_OnUserBulletDamage,PX_NULL);//处理伤害事件
return pObject;
}
//////////////////////////////////////////////////////////////////////////
//user Object
SOD_Play_Object_UserObject *SOD_Object_GetUserObject(PX_Object *pObject)
{
return (SOD_Play_Object_UserObject *)pObject->pObject;
}
//处理用户对象伤害事件
px_void SOD_Object_UserObject_OnDamage(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
SOD_Play_Object_UserObject *UserObject=SOD_Object_GetUserObject(pObject);
SOD_Play *pPlay=(SOD_Play *)pObject->User_ptr;
if (UserObject->life<=e.Param_int[0])//血扣完了
{
UserObject->life=0;
//删除对象
PX_WorldRemoveObject(&pPlay->world,pObject);
//创建爆炸效果
SOD_CreateExplosion(pPlay,PX_POINT(pObject->x,pObject->y,0),192);
pPlay->score+=100;
}
else
{
//扣除血量
UserObject->life-=e.Param_int[0];
UserObject->beAttackElpased=0;
}
}
px_void SOD_Object_UserObjectFree(PX_Object *pObject)
{
SOD_Play_Object_UserObject *puserObj=SOD_Object_GetUserObject(pObject);
//释放动画集
PX_AnimationFree(&puserObj->user_animation);
}
//对象更新
px_void SOD_Object_UserObjectUpdate(PX_Object *pObject,px_dword elpased)
{
SOD_Play *pPlay=(SOD_Play *)pObject->User_ptr;
SOD_Play_Object_UserObject *puo=SOD_Object_GetUserObject(pObject);
px_point shippt,user_v;
puo->atom_elpased+=elpased;
//假如飞船还存活
if (pPlay->shipObject->Visible)
{
if (SOD_ALIEN_SEARCH_DISTANCE>PX_PointDistance(PX_POINT(pObject->x,pObject->y,0),PX_POINT(pPlay->shipObject->x,pPlay->shipObject->y,0)))
{
//向飞船发射子弹
if (puo->atom_elpased>1000)
{
PX_Object *pfa;
puo->atom_elpased=0;
//发射子弹
pfa=SOD_Object_UserBulletCreate(pPlay,pObject->x,pObject->y,SOD_USER_ATOM_SPEED);
if (pfa)
{
PX_WorldAddObject(&pPlay->world,pfa);
}
//播放发射子弹音效
SOD_PlaySoundFromResource(pPlay->runtime,SOD_KEY_SND_ALIEN_FIRE,PX_FALSE);
}
}
//修正速度,追踪ship
shippt.x=pPlay->shipObject->x;
shippt.y=pPlay->shipObject->y;
shippt.z=0;
user_v=PX_PointSub(shippt,PX_POINT(pObject->x,pObject->y,0));
user_v=PX_PointMul(PX_PointUnit(user_v),SOD_USEROBJECT_SPEED);
puo->velocity=user_v;
}
puo->beAttackElpased+=elpased;
//依据速度更新当前位置
pObject->x+=puo->velocity.x*elpased/1000;
pObject->y+=puo->velocity.y*elpased/1000;
if (pObject->x<-SOD_STONE_GEN_SPACE-1||pObject->x>pPlay->world.world_width+SOD_STONE_GEN_SPACE+1||\
pObject->y<-SOD_STONE_GEN_SPACE-1||pObject->y>pPlay->world.world_height+SOD_STONE_GEN_SPACE+1\
)
{
//删除对象
PX_WorldRemoveObject(&pPlay->world,pObject);
}
//更新动画信息
PX_AnimationUpdate(&puo->user_animation,elpased);
}
//渲染对象
px_void SOD_Object_UserObjectRender(px_surface *psurface,PX_Object *pObject,px_dword elpased)
{
SOD_Play_Object_UserObject *pou=SOD_Object_GetUserObject(pObject);
if (pou->beAttackElpased<50)
{
PX_TEXTURERENDER_BLEND blend;
blend.alpha=1;
blend.hdr_B=0;
blend.hdr_G=0;
blend.hdr_R=1;
PX_AnimationRender(psurface,&pou->user_animation,PX_POINT(pObject->x,pObject->y,0),PX_TEXTURERENDER_REFPOINT_CENTER,&blend);
}
else
PX_AnimationRender(psurface,&pou->user_animation,PX_POINT(pObject->x,pObject->y,0),PX_TEXTURERENDER_REFPOINT_CENTER,PX_NULL);
}
PX_Object* SOD_Object_UserObjectCreate(SOD_Play *play,px_int life)
{
PX_Object *pObject;
SOD_Play_Object_UserObject *pou;
pou=(SOD_Play_Object_UserObject *)MP_Malloc(&play->runtime->runtime.mp_game,sizeof(SOD_Play_Object_UserObject));
if (pou==PX_NULL)
{
PX_ASSERT();
return PX_NULL;
}
pObject=(PX_Object *)PX_ObjectCreate(&play->runtime->runtime.mp_game,PX_NULL,0,0,0,0,0,0);
if (!pObject)
{
MP_Free(&play->runtime->runtime.mp_game,pou);
PX_ASSERT();
return PX_NULL;
}
pObject->x=play->world.world_width/2.0f;
pObject->y=play->world.world_height/2.0f;
pObject->z=SOD_ALIEN_Z;
pObject->Width=192;
pObject->diameter=192;
pObject->Height=192;
pObject->pObject=pou;
pObject->Type=SOD_OBJECT_TYPE_USEROBJECT;
pObject->ReceiveEvents=PX_TRUE;
pObject->Func_ObjectFree=SOD_Object_UserObjectFree;//回调事件
pObject->Func_ObjectUpdate=SOD_Object_UserObjectUpdate;
pObject->Func_ObjectRender=SOD_Object_UserObjectRender;
pObject->User_ptr=play;
pObject->impact_Object_type=SOD_IMPACTTEST_OBJECTTYPE_ENEMY;//碰撞类型
pObject->impact_test_type=0;//碰撞测试类型
pou->beAttackElpased=0;
pou->life=life;
//绑定动画集
PX_AnimationCreate(&pou->user_animation,PX_ResourceLibraryGetAnimationLibrary(&play->runtime->runtime.ResourceLibrary,SOD_KEY_ANIMATION_USERDEF));
PX_AnimationSetCurrentPlayAnimationByName(&pou->user_animation,SOD_ANIMATION_KEY_USERDEF_NORMAL);
pou->velocity=PX_POINT(1,0,0);
pou->atom_elpased=0;
PX_ObjectRegisterEvent(pObject,SOD_OBJECT_EVENT_DAMAGE,SOD_Object_UserObject_OnDamage,PX_NULL);
return pObject;
}
然后玩玩看:不好意思做着做着灵感就突然来了,把贴图换一下,
侵删,谢绝律师函
文章的最后
你可以:
在这里下载到游戏和它的完整源代码:
matrixcascade/PainterEnginespace old driver的代码就在对应名字的文件夹,别忘了:
下载后你可以在这里找到到游戏的Release版本,直接运行就可以了,到手直接开玩(避免律师函警告没发cxk版本)
或者:
加入PainterEngine交流QQ群:419410284,欢迎游戏开发者和C语言玩家加群交流,谢绝作业党伸手党,你可以在这里咨询关于游戏和PainterEngine的问题
附上笔者刚刚的战绩
欢迎点赞充电:
https://www.bilibili.com/video/av57796335/ |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|