8.弹幕系统

弹幕系统概述:

弹幕系统的设计体现了射击游戏的基本要素,玩家要在敌人放出的大量子弹(弹幕)的细小空隙间闪避,能在玩家闪躲弹幕的时候给玩家带来快感,接近满屏的子弹,增加了对玩家的视觉冲击力。

弹幕系统原理:

每一个敌机都持有一个弹幕实例,每个弹幕实例中包含多个子弹实例,通过配置弹幕的属性,使每个子弹实例在轨迹管理器的作用下,形成一种有规律性的直线运动,在视觉上给玩家展现出弹幕的效果。如图8-1所示。


图8-1

实现方法:

步骤1:

子弹类,定义子弹的属性和借口。

01 public class Bullet
02 {
03     //从模板创建子弹Actor
04     public void CreateActor ()
05     {
06         _obj = ActorManager.CreateFromTemplate(@"asset:bullet01.template",false);
07  
08     }
09  
10     //激活,显示子弹
11     public void ActiveObj (Vector2 startPos)
12     {
13         _obj.Active();
14         if (_obj.GetChildCount()>0)
15         {
16             _obj.GetChild(0).Active();
17         }
18  
19         _obj.WorldPosition = new Vector3 (startPos.X,startPos.Y,2.0f);
20     }
21  
22     //隐藏子弹
23     public void DeactiveObj ()
24     {
25         _obj.Deactive();
26         if (_obj.GetChildCount()>0)
27         {
28             _obj.GetChild(0).Deactive();
29         }
30     }
31     private Actor _obj = new Actor();
32     private UInt32 _id; 
33     private UInt32 _target_id;
34     private Vector2 _startPos;
35     private bool _isShooted = false;
36 }

步骤2:

配置弹幕发射子弹的属性。

01 public class Barrage
02   {
03       //发射子弹
04       public void ShootBullet (float elapsedTime, Vector3 pos)
05       {
06           timer +=  elapsedTime;
07           if (timer >= _shoot_interval && _needshoot_id <= (Bullets.Count - 1))
08           {
09               Vector2 posV2 = new Vector2(pos.X,pos.Y);
10               Bullets[_needshoot_id].ActiveObj(posV2);
11               Bullets[_needshoot_id].SetShooted(true);
12               _needshoot_id ++;
13               timer = 0.0f;
14           }
15       private UInt32 _id;
16       private UInt32 _obj_id;                  
17       private float _start_speed;
18       private float _accel_speed;
19       private float _shoot_interval;
20       private float _shoot_direction;
21       private float _direction_offset;
22       private List< Bullet> Bullets; 
23       private TrajectoryType _tt ;
24       private float timer = 0.0f;
25       private int _needshoot_id = 0;
26       private Actor _owner;
27       }
28   }

步骤3:

设计弹幕管理器,管理每一个弹幕实例的发射。

01 public class BarrageMgr
02   {
03       //请求子弹
04       public bool AskForBullets (int count, List< Bullet> bullets, Actor owner)
05       {
06           if (ReloadBullet.Count == 0)
07           {
08               return false;
09           }
10  
11           if (count >= ReloadBullet.Count)
12           {
13               count = ReloadBullet.Count;
14           }
15  
16           for (int i = 0; i < count; i++)
17           {
18               ReloadBullet[i].DeactiveObj();
19               Vector2 pos = new Vector2(owner.WorldPosition.X,owner.WorldPosition.Y);
20  
21               ReloadBullet[i].setPos(pos);
22               bullets.Add(ReloadBullet[i]);
23  
24           }
25           ReloadBullet.RemoveRange(0,count);
26           return true;
27       }
28       //处理轨迹
29       public void DealTrajectory (Barrage barrage,float elapsedTime)
30       {
31           _trajectoryMgr.MoveBarrage(barrage,elapsedTime);   
32       }
33       //更新弹幕位置
34       public void Tick(float elapsedTime)
35       {
36           foreach (KeyValuePair< uint,Barrage> pair in _barrageDict)
37           {
38  
39               Barrage barrage = pair.Value;
40    
41               DealTrajectory(barrage,elapsedTime);
42               barrage.DestroyBullet();
43               if (!barrage.IsOwnerActive() && barrage.IsAllBulletsDeactive())
44               {
45                   barrage.Reload();
46               }
47           }
48           Debug.Dbgout( _barrageDict.Count.ToString() );
49       }
50  
51   }

步骤4:

设计轨迹管理器,使子弹形成一种有规律性的直线运动。

01 public class Trajectory
02 {
03     //直线轨迹算法
04     public static Vector2 GoStraight(Vector2 start_pos, float direction,
05                                      float start_speed, float accel_speed, float use_time, outVector2 pos)
06     {
07         float angle = direction * (float)Math.PI / 180.0f;
08         float seconds = (float)use_time;
09         float move_length = start_speed * seconds + accel_speed * seconds * seconds / 2.0f;
10         pos.X = move_length * (float)Math.Cos(angle) + start_pos.X;
11         pos.Y = move_length * (float)Math.Sin(angle) + start_pos.Y;
12         return pos;
13     }
14      
15     //这里的跟踪算法主要适用于匀速圆周运动类型的要跟踪,速率不变,一定的旋转角度
16     //追踪轨迹算法
17     public static Vector2 Tracking(Vector2 start_pos, ref float direction, Vector2 dest_pos,
18                                    float start_speed, float accel_speed, float track_degree,float use_time)
19     {
20         Vector2 newpos = new Vector2(0, 0);
21          
22         if (direction < 0)
23         {
24             direction += 360;
25         }
26         else
27         {
28             direction = direction % 360;
29         }
30          
31         //判断目标与飞行的夹角
32         float degree =(float) (Math.Atan2(dest_pos.Y - start_pos.Y,
33                                    dest_pos.X - start_pos.X) * 180 / Math.PI);
34         if (degree < 0)
35         {
36             degree += 360;
37         }
38          
39         //最小目标夹角
40         float dest_degree = (float)Math.Abs(degree - direction) % 360;
41         if (dest_degree > 180)
42         {
43             dest_degree = 360 - dest_degree;
44         }
45         if (dest_degree < 0.000001)
46         {
47             GoStraight(start_pos, direction, start_speed, accel_speed, use_time,out newpos);
48             return newpos;
49         }
50          
51         //计算最终旋转的夹角
52         float use_seconds = use_time / 1000.0f;
53         float rotate_degree = track_degree * use_seconds;
54         if (rotate_degree > dest_degree)
55         {
56             rotate_degree = dest_degree;
57         }
58         double inner_degree = degree - direction;
59         if (inner_degree > 180)
60         {
61             direction -= rotate_degree;
62         }
63         else if (inner_degree <= 180 && inner_degree >= 0)
64         {
65             direction += rotate_degree;
66         }
67         else if (inner_degree < -180)
68         {
69             direction += rotate_degree;
70         }
71         else if (inner_degree >= -180 && inner_degree <= 0)
72         {
73             direction -= rotate_degree;
74         }
75              
76         GoStraight(start_pos, direction, start_speed, accel_speed, use_time, out newpos);
77         return newpos;
78     }  
79  
80 }


引擎官方网站:http://www.genesis-3d.com.cn/

官方论坛:http://bbs.9tech.cn/genesis-3d/

官方千人大群:59113309   135439306

YY频道-游戏开发大讲堂(完全免费,定期开课):51735288

Genesis-3D开源游戏引擎:游戏起源,皆因有我!!!

 


《Genesis-3D开源游戏引擎完整实例教程-2D射击游戏篇08:弹幕系统》本系列完结的更多相关文章

  1. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程:简介及目录》(附上完整工程文件)

    介绍:讲述如何使用Genesis-3D来制作一个横版格斗游戏,涉及如何制作连招系统,如何使用包围盒实现碰撞检测,软键盘的制作,场景切换,技能读表,简单怪物AI等等,并为您提供这个框架的全套资源,源码以 ...

  2. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程04:技能的输入与检测》

    4.技能的输入与检测 概述: 技能系统的用户体验,制约着玩家对整个游戏的体验.游戏角色的技能华丽度,连招的顺利过渡,以及逼真的打击感,都作为一款游戏的卖点吸引着玩家的注意.开发者在开发游戏初期,会根据 ...

  3. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程01: 资源导入》

    1. 资源导入 概述: 制作一款游戏需要用到很多资源,比如:模型.纹理.声音和脚本等.通常都是用其它相关制作资源软件,完成前期资源的收集工作.比如通常用的三维美术资源,会在Max.MAYA等相应软件中 ...

  4. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程08:虚拟键盘实现》--本系列完结

    8.虚拟键盘实现 概述: 硬键盘就是物理键盘,平时敲的那种.软键盘是虚拟的键盘,不是在键盘上,而是在"屏幕"上.虚拟按键就是虚拟键盘的一部分,根据功能需求,提供部分按键效果的UI可 ...

  5. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程07:UI》

    概述: UI即User Interface(用户界面)的简称.UI设计是指对软件的燃机交互.操作逻辑.界面美观的整体设计.好的UI设计不仅可以让游戏变得更有品位,更吸引玩家,还能充分体现开发者对游戏整 ...

  6. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程06:技能播放的逻辑关系》

    6.技能播放的逻辑关系 技能播放概述: 当完成对技能输入与检测之后,程序就该对输入在缓存器中的按键操作与程序读取的技能表信息进行匹配,根据匹配结果播放相应的连招技能. 技能播放原理: 按键缓存器中内容 ...

  7. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程05:技能读表》

    5.技能读表 技能读表概述: 技能读表,作为实现技能系统更为快捷的一种方式,被广泛应用到游戏开发中.技能配表,作为桥梁连接着游戏策划者和开发者在技能实现上的关系.在游戏技能开发中,开发者只需要根据策划 ...

  8. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程03:碰撞检测》

    3.碰撞检测 碰撞检测的概述: 碰撞在物理学中表现为两粒子或物体间极端的相互作用.而在游戏世界中,游戏对象在游戏世界自身并不受物理左右,为了模拟真实世界的效果,需要开发者为其添加属性,以模拟真实事件的 ...

  9. 《Genesis-3D开源游戏引擎--横版格斗游戏制作教程02:关键帧动画导入与切割》

    2. 关键帧动画导入与切割 动画的分割与导入概述: 在游戏当中,游戏角色在不同状态下会有不同的动作,这些动作在引擎里相当于一段段的动画片段.当导入模型资源的时候,连同模型动画都会一并导入到引擎中.开发 ...

  10. Beat &#39;Em Up Game Starter Kit (横版格斗游戏) cocos2d-x游戏源代码

    浓缩精华.专注战斗! 游戏的本质是什么?界面?养成?NoNo!    游戏来源于对实战和比赛的模拟,所以它的本源就是对抗.就是战斗! 是挥洒热血的一种方式! 一个游戏最复杂最难做的是什么?UI?商城? ...

随机推荐

  1. Android:查看应用创建的数据库

    每个Android应用程序都可以使用SQLite数据库.它创建的位置在data/data/<项目文件夹>/databases/ 运行后打开,window->show view-> ...

  2. PowerDesigner连接Oracle数据库建表序列号实现自动增长

    原文:PowerDesigner连接Oracle数据库建表序列号实现自动增长 创建表就不说了.下面开始介绍设置自动增长列. 1 在表视图的列上创建.双击表视图,打开table properties — ...

  3. C++:向函数传递对象(对象、对象指针、对象引用)

    3.5.1   使用对象作为函数参数,其方法与传递基本类型的变量相同 //例3.21 使用对象作为函数参数 #include<iostream> using namespace std; ...

  4. GridView 和ListView中自适应高度

    android中GridView  和ListView放在scrollView中时会默认的只有一行高这时就要我们自己计算出它的高度啦 首先是listview的 //动态设置listview的高度 pu ...

  5. PHP比你想象的好得多

    有很多对于PHP的抱怨,甚至这些抱怨也出自很多聪明的人.当Jeff Atwood写下对于PHP的另一篇抱怨文章之后,我思考了下PHP的好的方面. 这些抱怨最大的问题是他们出自很多仍在使用旧版本PHP的 ...

  6. Burnside引理和polay计数学习小记

    在组合数学中有这样一类问题,比如用红蓝两种颜色对2*2的格子染色,旋转后相同的算作一种.有多少种不同的染色方案?我们列举出,那么一共有16种.但是我们发现,3,4,5,6是同一种,7,8,9,10是用 ...

  7. Android开发之定义接口暴露数据

    写了一个网络请求的工具类,然后想要获取到网络请求的结果,在网络工具类中写了一个接口,暴露除了请求到的数据 代码: package com.lijingbo.knowweather.utils; imp ...

  8. ACM - ICPC World Finals 2013 B Hey, Better Bettor

    原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 这题真心的麻烦……程序不长但是推导过程比较复杂,不太好想 ...

  9. 函数get_table_share

    得到一个table_share 1)先从table_def_cache中查找, 如果有, 直接返回 2)如果没有找到,    为table_share分配内存,初始化,打开.frm文件,并将share ...

  10. bzoj1185

    一遇到数学题和计算几何题我就要调半天…… 玛雅,我真是太弱了…… 基本思路很简单,先上凸包,然后矩形与凸包一边重合,然后旋转卡壳即可 然而我没怎么写过计算几何题,一开始写的各种囧,后来看了hzwer的 ...