【flash】FPS游戏枪的后坐力制作 准星的抖动
游戏里的准星抖动 大体思路是这样子的。所谓的手感 也不过是 函数产生的 偏移大小而已。
代码下载在最下面
效果
不压枪
是一个7字
压枪 哎算了 好久就没打fps 压了个寂寞
废话不说 上代码
先分析下
Fps里面 枪的后坐力是怎么回事。
射击点 其实不是中心,而是十字准星 之间的一个范围产生了一个随机落点。 [这也就是为什么你 走路 准星会变大 蹲下准星会变小的原因。( 然后对这个落点 进行射线检测(3维 里射线检测是 检测这个点 和前方是都会碰撞到敌人)
后坐力 后坐力在3维游戏里的表现是 镜头的抖动。
这里是2d的 也就是 准星变化。
这个变化 包括 准星位置x,y 准星大小
他们都是基于随机数完成的。
实现的思路
鼠标按下 --射击 准星抖动
鼠标松开 准星回正
射击
射击是的随机落点
首先获得准星中心坐标。
然后在这个中心坐标产生随机数。或者最后的坐标。
当然这个随机数应该是 根据准星的大小来变化的。准星越大 这个数字产生的范围越高
x0 = x+随机数
y0 = y+随机数
准星的抖动
应该用一个数组来储存 准星的偏移量。例如 shakeValue=[0,0]
当你按下鼠标 每射击一次 他就自增一个随机数。
准星的最终坐标 应该是由 抖动数组 和鼠标坐标得到的。
同时 他应该有一个最大的抖动值。当抖动数组的值超过一个阈值的时候 他就会应该再抖动。
准星 大小变化也是同理。
准星回正
鼠标松开
我们一个 吧shakevalue 的数字归0
让他向0靠近。
代码
时间轴的主函数
第1帧
import flash.events.*;
// 接收输入 吧输入的数据储存在 类里面
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,mouseUpHandler)
stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoveHandler)
function mouseDownHandler(e:MouseEvent):void{
GlobalInput.mouseDown = true;
}
function mouseUpHandler(e:MouseEvent):void{
GlobalInput.mouseDown = false;
}
function mouseMoveHandler(e:MouseEvent):void{
GlobalInput.mousePos[0] = e.stageX
GlobalInput.mousePos[1] = e.stageY
}
// 产生准星
var taget = new Sight(this)
addChild(taget)
第3帧
taget.Tick()
gotoAndPlay(2)
每帧调用 准星的 taget 函数
Tick() 函数可以看成是 taget的主函数 看代码逻辑看他就行了
接收输入的类
静态类
package {
public class GlobalInput {
public static var mousePos:Array=[0,0] // 鼠标坐标
public static var mouseDown:Boolean=false; // 鼠标是否按下
}
}
准星类
关于准星所有的东西都在这里,太长了 看起来费劲,代码解析 在这下面
package {
import Math
public class Sight extends sight {
var stage_
var shakeYMax = 110
var shakeXMax = 40 //X Y 最大偏移
var shakeValue = [0,0] //准星偏移
var _scale = 1
var max_scale = 2 //准❤最大缩放
var range = 2 //子弹落点偏移
public function Sight(stage) {
this.stage_ = stage
}
public function Tick(){
if (GlobalInput.mouseDown){
shake() // 枪口抖动,抖动的范围 应该是一个
fire()
}
else
correction()
this.stage_.addChild(this)
}
function shake(){
if (Math.abs(shakeValue[0])< shakeXMax ){
this.shakeValue[0]+= randint(0,10)* [1,-1][randint(0,1)]
}
else if (shakeValue[0]>0){
this.shakeValue[0]+= randint(0,10)*-1
}
else if (shakeValue[0]<0)
{
this.shakeValue[0]+= randint(0,10)*1
}
if (Math.abs(shakeValue[1])< shakeYMax ){ //y抖动
this.shakeValue[1]+= randint(0,10)*-1
}
else
this.shakeValue[1]-= randint(0,3)*-1
if (this._scale < this.max_scale)
this._scale += Math.random()/30+0.01
else
this._scale -= Math.random()/80+0.01
this.scaleX =this._scale
this.scaleY =this._scale
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
}
function correction(){
if (this.shakeValue[1] < 0)
this.shakeValue[1]+= randint(20,30)
else
this.shakeValue[1] = 0
if (this.shakeValue[0] != 0){
if (Math.abs(this.shakeValue[0]) < 3)
this.shakeValue[0] = 0
if (this.shakeValue[0] > 0)
this.shakeValue[0] -=randint(3,6)
else if (this.shakeValue[0] < 0)
this.shakeValue[0] +=randint(3,6)
}
if (this._scale > 1)
{
this._scale -= this._scale += Math.random()/20
}
else
this._scale = 1
this.scaleX = this._scale
this.scaleY = this._scale
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
}
function fire(){
var spawnPos:Array = [0,0]
spawnPos[0] = randfloat(0,this.range * this._scale*3 ) * [1,-1][randint(0,1)]
spawnPos[1] = randfloat(0,this.range * this._scale*3 ) * [1,-1][randint(0,1)]
var b = new bullte()
b.x = this.x + spawnPos[0]
b.y = this.y + spawnPos[1]
this.stage_.addChild(b)
}
function randint(min,max):int{
return Math.round(Math.random()*(max-min))+min
}
function randfloat(min,max):Number{
return Math.random()*(max-min)+min
}
}
}
工具函数
randint 产生一个 min,max 的随机整数
randfloat 产生一个min 到 max 的随机小数
这2个用来求抖动用的。
function randint(min,max):int{
return Math.round(Math.random()*(max-min))+min
}
function randfloat(min,max):Number{
return Math.random()*(max-min)+min
}
类的属性 用到的一些数据
stage_ 构造函传入的舞台,为了获得舞台上的一些参数
shakeValue 准星的偏移数组。关于准星的偏移数据都存在这里
shakeYMax shakeXMax 抖动的最大值。当偏移数组的知道大于这个 就不会再抖动
range 子弹的落点范围。以准星中心为坐标,半径为range 产生随机落点。
_scale 准星的真正比例
max_scale 准星的最大缩放抖动的时候 超过这个就不会继续放大。
this.x this.y 是准星的真正位置
var stage_
var shakeYMax = 110
var shakeXMax = 40 //X Y 最大偏移
var shakeValue = [0,0] //准星偏移
var _scale = 1
var max_scale = 2 //准❤最大缩放
var range = 2 //子弹落点偏移
主函数 和 主要功能实现
主函数
如果 鼠标按下就
开火+抖动
否则 就执行 准星修正
public function Tick(){
if (GlobalInput.mouseDown){
shake() // 枪口抖动,抖动的范围 应该是一个
fire()
}
else
correction()
this.stage_.addChild(this)
}
准星抖动
准星的抖动 我分成了3部分 x方向上的抖动 y 方向上的抖动 和缩放
x方向上的抖动
[1,-1][randint(0,1)] 这一段可能有人看不懂,我分解下
var lis1 = [1,-1],index = randint(0,1)
得到一个值 list1[index] 实际上就是从[1,-1]随机选择一个出来。
x的抖动是分左右的。
逻辑
if (x方向上准星偏移绝对值 < 最大值)
准星的x偏移+= 一个随机数 * 1或者-1 (1代表右边 -1代表左边)
else if 已经到达最大值 且 在右边
让准星往左边偏移一点
else if 已经到最大值且 准星在坐标
让准星往右边偏移一点
为什么要处理边界呢?假设你准星偏移到边界,你不继续偏移了。他就会卡在那里不动 很傻。
y抖动
if (y方向上准星偏移 < 最大值)
准星的y偏移+= 一个随机数
else if 已经到达最大值
让他往下偏移一点点
缩放
if 缩放值<缩放最大值
缩放值 随机的变大
else 已经达到追到
缩放值变小了一点点’
为什么要处理边界 上面的x有说。
接着 我们求完偏移数组,和准星缩放 应该让他 赋值给 真正的准星位置上。
this.scaleX 是准星影片剪辑的属性。他决定了准星的大小
this.scaleX =this._scale
this.scaleY =this._scale
this.x 不用说了 他就决定了准星显示的真正坐标。
解释下
this.x = 鼠标的x位置 + 准星的x偏移
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
if (Math.abs(shakeValue[0])< shakeXMax ){
this.shakeValue[0]+= randint(0,10)* [1,-1][randint(0,1)]
else if (shakeValue[0]>0)
this.shakeValue[0]+= randint(0,10)*-1
else if (shakeValue[0]<0)
this.shakeValue[0]+= randint(0,10)*1
if (Math.abs(shakeValue[1])< shakeYMax ) //y抖动
this.shakeValue[1]+= randint(0,10)*-1
else
this.shakeValue[1]-= randint(0,3)*-1
if (this._scale < this.max_scale)
this._scale += Math.random()/30+0.01
else
this._scale -= Math.random()/80+0.01
this.scaleX =this._scale
this.scaleY =this._scale
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
开火函数
开火函数 实现了 对开火坐标的确定 和 子弹的产生。
[1,-1][randint(0,1)] 上面有解释 自己看。
spawnPos 是产生子弹最终目标 x y
首先我们确定 子弹是在准星中心 附近 产生的。而且 随着准星增大范围越大。
计算公式 = 基准范围 * 缩放比例 * 方向
randfloat(0,this.range * this._scale* 3 ) 求的就是 基准范围 * 缩放比例
[1,-1][randint(0,1)] 是方向
b的位置的确定,为什么是 this.x呢?==
主函数调用的顺序是 先抖动后fire()
因为 this.x y 就是准星的真正的中心位置
b.x = this.x + spawnPos[0]
b.y = this.y + spawnPos[1]
function fire(){
var spawnPos:Array = [0,0]
spawnPos[0] = randfloat(0,this.range * this._scale*3 ) * [1,-1][randint(0,1)]
spawnPos[1] = randfloat(0,this.range * this._scale*3 ) * [1,-1][randint(0,1)]
var b = new bullte()
b.x = this.x + spawnPos[0]
b.y = this.y + spawnPos[1]
this.stage_.addChild(b)
}
回正函数
分为 x y 修正 和 比例修正
x y 主要是吧 偏移数组归为1
scale 主要是 吧他变为1
注意在x 修正的时候需要注意方向问题。
这里说一下y的修正
啊 忘记说了 这里y偏移是 负数。因为 y坐标轴是向下的。
if (如果y偏移 小于0)
y偏移 +=一个随机数
else(如果大于0 说明修正过头了)
y 偏移 = 0
同理 修正 需要检测是否修正过头,过头了 就把他变成 最终值。
scale的最终值是1
x y 偏移的最终值是0
最后是吧计算出来的修正值赋值给准星
this.scaleX = this._scale
this.scaleY = this._scale
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
function correction(){
if (this.shakeValue[1] < 0)
this.shakeValue[1]+= randint(20,30)
else
this.shakeValue[1] = 0
if (this.shakeValue[0] != 0){
if (Math.abs(this.shakeValue[0]) < 3)
this.shakeValue[0] = 0
if (this.shakeValue[0] > 0)
this.shakeValue[0] -=randint(3,6)
else if (this.shakeValue[0] < 0)
this.shakeValue[0] +=randint(3,6)
}
if (this._scale > 1){
this._scale -= this._scale += Math.random()/20
}
else
this._scale = 1
this.scaleX = this._scale
this.scaleY = this._scale
this.x = GlobalInput.mousePos[0] + this.shakeValue[0]
this.y = GlobalInput.mousePos[1] + this.shakeValue[1]
}
完
抓住主要矛盾
if 按下
准星偏移
开火
else
准星回正
这个只是基本效果,真正玩游戏的时候 你会发现一开始的几枪 一般是不会偏的 比如MP5 。 这时候需要多加点判断喽
例如一个count 当他是1-3 的时候 不执行准星抖动
可以感觉到 游戏机制 的背后还是算法,和数据的计算 和语言没多大关系
个人的总结,废话有点多。见谅
如果有说错的地方 请大佬斧正
代码链接
链接:https://pan.baidu.com/s/1q70GL2bZ92CGBP1YRTCspg
提取码:eps9
上一篇: 【flash基础】简单的 打飞机小游戏