Flash-APE物理引擎学习<二>
昨天整理了下代码,新的Demo在这里:
在说具体实现之前,丢个Flash物理引擎在产品展示中的应用:http://www.carlosulloa.com/.
这是一款汽车产品的展示平台,用方向键控制机车移动,点鼠标左键复位汽车初始位置。
不过,这个不是2d物理引擎完成的,而是基于PV3D的,,,,恩,3D物理引擎是在比较难搞……有时间尝试整理下……
下边来说下Demo的具体实现,首先要收下物理引擎,物理引擎是干嘛用的呢?
物理引擎是一个处理物理现象的引擎。
那什么是物理现象呢?
举个例子,一个风筝在天上飞,它会受到风力,阻力,摩擦力,还有重力,绳子的作用力,etc。。。而物理引擎就是帮你处理这些力的一个框架集。
你要做的就是给它个重力,然后都作用在风筝上,至于这些里之间的相互作用,都由引擎来搞定。
下边来代码,先是文档类,也就是主容器了:
package org.nwhy.bubble_bobble{
import flash.display.*;
import flash.events.Event;
import org.cove.ape.*;
public class BubbleBobble extends Sprite{
private var bulletHolder:Group;
public function BubbleBobble(){
init();
}
private function init(){
initStage();
initGroups()
}
private function initStage(){
stage.scaleMode = StageScaleMode.NO_SCALE;//设置为不缩放
//stage.align = StageAlign.TOP_LEFT;
stage.frameRate = 55;//设置帧频
stage.addEventListener(Event.ENTER_FRAME, run);
}
private function initGroups(){
//初始化2D物理引擎
APEngine.init(1/4);
APEngine.container = this;
APEngine.addForce(new VectorForce (false,0,1));//加上重力
//放bullet的容器,bullet就是那个各种颜色的球~
bulletHolder = new Group(true);
APEngine.addGroup(bulletHolder);
//加入墙壁
var wall:Wall = new Wall();
APEngine.addGroup(wall);
//gun,就是下边那个大伯说有点像啥的那个啥……囧一个
var gunGroup:GunGroup = new GunGroup(stage);
APEngine.addGroup(gunGroup);
//设置bullet的碰撞检测内容
bulletHolder.addCollidableList(new Array(wall,gunGroup));
}
private function run(e:Event) {
APEngine.step();
APEngine.paint();
}
//增加bullet的对外接口
public function addBullet(bullet:CircleParticle){
bulletHolder.addParticle(bullet);
}
}
}
这里边干的事就是加入各种Group(APE物理引擎里的碰撞单元是Particle,单个或多个Particle组成Group),至于Group里具体是啥Group里自己处理。
Flash – APE物理引擎学习<一>
APE是什么就不介绍了,下边先上Demo:
点击鼠标发射子弹,击中那个囧就过关了。
代码实现其实也很简单,APE里边所有的Item都是以Group的形式存在。
在这个Demo里,上边的5个球是一组,例子里是Avatar类,里边有5个WheelParticle,不知道为何CircleParticle没有旋转的。
AS3-类似于机战的角色移动(基于Tile)
先来看下Demo:
源代码下载地址:http://dl.dropbox.com/u/477487/flash/game/mapMove.rar
只做了移动,重叠和阻挡的部分没做,短时间内不会有时间做的=,=
主要逻辑是这样,选中Role就显示可以行走的范围,选其中一点之后Role就移动。
下边看下简单的实现。主要是OverlayView.as和Role.as,我很懒,其他代码大家自己看……
AS3-aSpaceEscape 迷宫脱离游戏(四)整合部分
前边咱讲了地图和角色的生成以及逻辑代码部分,接下去就是2者的整合了。
这个整合的类我把它命名成Controller.as,在主场景的时间轴里写上:
import org.nwhy.aSpaceEscape.*; var controller:Controller = new Controller(stage,0); addChild(controller);
然后是Controller.as:
package org.nwhy.aSpaceEscape{
import flash.display.Sprite;
import flash.display.Stage;
public class Controller extends Sprite{
private var mStage:Stage;
public static var level:uint;//等级
private var role:Role;
public function Controller(stage:Stage,levelID:uint){
mStage = stage;
level = levelID;
drawMap();
drawRole();
}
private function drawMap(){
var map:Map = new Map(Config.MAP[level]);
addChild(map);
}
private function drawRole(){
role = new Role(mStage);
addChild(role);
}
}
}
代码内容很简单,画一个Map,然后画一个Role,就完成了~
整个游戏的代码地址(Full source):http://dl.dropbox.com/u/477487/flash/game/aSpaceEscape.rar
AS3-aSpaceEscape 迷宫脱离游戏(三)角色部分
刚我们讲了地图,看起来还是挺简单的,只要输入一个数组,或者说是一个地图矩阵块,一个地图就被生成了。
那下边我们无敌的主角就要上场了,就是那个看起来在大笑,那个很嚣张的东东,我们就起个类名叫Role好了,当然你也可以把这个类叫JJ,GG,MM,DD,etc~
跟角色有关的逻辑应该尽量都放在这个类内部,为啥说应该尽量呢,有些时候有变态的特殊需求嘛。
好,来看Role.as:
package org.nwhy.aSpaceEscape{
import flash.display.Sprite;
import flash.display.Stage;
import flash.geom.Rectangle;
import flash.events.*;
public class Role extends Sprite {
private var mStage:Stage;//根场景的stage;
private var level:uint;
private var mSpeed:int = Config.TILE_SIZE;//移动速度
private var xSpeed:int;
private var ySpeed:int;
private var mDown:Array = [0,0,0,0];//按键组合,只需要上下左右不需要45度角,so...
private var isKeyDown:Boolean;
public function Role(stage:Stage):void {
level = Controller.level;
mStage = stage;
reset();
//添加事件监听
addEventListeners();
}
private function addEventListeners():void{
mStage.addEventListener(KeyboardEvent.KEY_DOWN, mKeyDownHandler);//Keyboard事件
mStage.addEventListener(KeyboardEvent.KEY_UP, mKeyUpHandler);
addEventListener(Event.ENTER_FRAME, mUpdate);
}
private function mKeyDownHandler(e:KeyboardEvent):void{
if(!isKeyDown){
switch (e.keyCode) {
case 37 :
mDown[0]=1;
break;
case 38 :
mDown[1]=1;
break;
case 39 :
mDown[2]=1;
break;
case 40 :
mDown[3]=1;
break;
}
isKeyDown = true;
}
checkKeyDown(mDown.join(""));
}
private function mKeyUpHandler(e:KeyboardEvent):void {
switch (e.keyCode) {
case 37 :
mDown[0]=0;
break;
case 38 :
mDown[1]=0;
break;
case 39 :
mDown[2]=0;
break;
case 40 :
mDown[3]=0;
break;
}
}
private function checkKeyDown(k:String){
switch(k){
case "1000" :
//trace("left");
xSpeed = - mSpeed;
ySpeed = 0;
break;
case "0100" :
//trace("top");
xSpeed = 0;
ySpeed = - mSpeed;
break;
case "0010" :
//trace("right");
xSpeed = mSpeed;
ySpeed = 0;
break;
case "0001" :
//trace("down");
xSpeed = 0;
ySpeed = mSpeed;
break;
}
}
private function mUpdate(e:Event){
var posX:int = (x+xSpeed)/mSpeed;
var posY:int = (y+ySpeed)/mSpeed;
if(posX>=Config.TILE_PERLINE || posY>=Config.TILE_PERLINE || posX< =-1 || posY<=-1){
trace("out of stage");
xSpeed = 0;
ySpeed = 0;
reset();
isKeyDown = false;
}else if(Config.MAP[level][posY][posX] > 1 && Config.MAP[level][posY][posX] !=5){
xSpeed = 0;
ySpeed = 0;
isKeyDown = false;
//trace(posX,posY,Config.MAP[level][posY][posX]);
}else if(Config.MAP[level][posY][posX] == 5){
trace("succ");
x += xSpeed;
y += ySpeed;
xSpeed = 0;
ySpeed = 0;
}
x += xSpeed;
y += ySpeed;
}
private function reset(){
x = Config.ROLE_POSITION[Controller.level][0] * Config.TILE_SIZE;
y = Config.ROLE_POSITION[Controller.level][1] * Config.TILE_SIZE;
}
}
}
AS3-aSpaceEscape 迷宫脱离游戏(二)地图生成部分
这个游戏一开始,我们需要一个地图类,用来生成地图,这里我用的是方格的形式,每个方格Tile使用的是同一个类Tile.as,至于不同的Tile素材,只要在库里弄个元件,不同帧放上不同的素材,然后绑定Tile类就可以了。
Map类的代码是这样:
package org.nwhy.aSpaceEscape{
import flash.display.Sprite;
public class Map extends Sprite{
private var data:Array;
public function Map(mapData:Array){
data = mapData;
drawMap();
}
private function drawMap(){
var len:uint = data.length;
var tile:Tile;
for(var i:uint=0;i<len;i++){
for(var j:uint=0;j<Config.TILE_PERLINE;j++){
tile = new Tile(data[i][j]+1);
tile.x = j * Config.TILE_SIZE;
tile.y = i * Config.TILE_SIZE;
addChild(tile);
}
}
}
}
}
填充的Tile.as:
package org.nwhy.aSpaceEscape{
import flash.display.MovieClip;
public class Tile extends MovieClip{
private var tileID:uint;
public function Tile(id:uint){
tileID = id;
showTile();
}
private function showTile(){
gotoAndStop(tileID);
}
}
}
代码是相当的简单。给个tileID 然后跑到指定帧就可以了~
然后是配置文件:
package org.nwhy.aSpaceEscape{
public class Config{
public static const TILE_SIZE:uint = 30;
public static const TILE_PERLINE:uint = 12;//指定每行的Tile数,列数并不固定
public static const MAP:Array = [[[0,0,0,0,0,0,0,0,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,2,1,0],
[0,1,1,2,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,5,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,1,2,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,2,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,1,1,1,1,1,1,1,1,1,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0]]];
public static const ROLE_POSITION:Array = [[2,2]];
}
}
AS3-aSpaceEscape 迷宫脱离游戏(一)分析
分享知识点:
1.Flash里的代码(Class,Actionscript)和库中的UI元件(MovieClip,Sprite)如何绑定。
2.KeyboardEvent,按键事件的绑定和处理。
3.如何把一个库中的UI元件加入到场景中。
4.stage的使用。
5.视图的刷新。
最近在想,做点什么Flash的分享好。在上一个周末,玩了一个Flash小游戏,觉得还不错,就写了一个。
来来来,咱来看一下Demo:
先来说说游戏的玩法,用小键盘的上下左右键控制移动那个看起来很嚣张的怪物,话说这个怪物是啥我也不知道……XX表情里扣出来的……
怪物只能直线移动,中途不能换方向,碰到树桩就会停下,跑出屏幕外就挂了(接着会被搬回到出发点),跑到Exit的地方就过关了。
是个很简单的游戏对吧,那应该怎么来实现呢?Follow me~
首先我们来整理一下需要的东西(PS:不包括素材):
1.怪物对象(Role Class);就是那个很嚣张的怪物
2.地图对象(Map Class);看到的全部方块的集合,包括树,出口
3.填充地图的对象(Tile Class);树,出口等
4.参数配置对象(Config Class/XML);
5.控制器(Controller Class);场景控制
6.etc;占位,可能会需要的东西
AS3-getDefinitionByName
先来看下CS3版F1的介绍:
public function getDefinitionByName(name:String):Object
语言版本 : ActionScript 3.0
Player 版本 : Flash Player 9
返回 name 参数指定的类的类对象引用。
参数 name:String — 类的名称。
返回 Object — 返回 name 参数指定的类的类对象引用。
引发 ReferenceError — 不存在具有指定名称的公共定义。
下边是一个读取地图信息并显示的实例。文件有点多,先来看主文档,TD.as:
package{
import flash.display.Sprite;
import org.nwhy.TD.Config;
import org.nwhy.TD.Controller;
public class TD extends Sprite{
public var screen:Sprite;
public function TD(){
screen = new Sprite();
addChild(screen);
var game:Controller = new Controller(screen,new Config());
}
}
}
AS3-连连看的主要算法
这几天一直在忙的一个东西,因为时间的关系,只弄了主要的算法,计时,分数,划线,点击效果等内容都没有管=.=
先来看连连看的规则分析,这里将选中的2个Obj分3种情况来讨论,一是同位于X轴,二是同位于Y轴,三是不同X,Y轴。
而第三种情况其实又可以归结到第1和第2种情况,具体的操作是先判断是否同X,Y轴,是的话先检测能否直连,不能的话判断不直连的情况.
如果两者属于不同的X,Y轴,那么先判断通过一点连接,然后再判断通过两点连接的情况,这里图方便用了最烂的方法.
AS3-子弹躲避游戏(下)
接上一篇《AS3-子弹躲避游戏(上)》。
有了Enemy和Role之后,自然就是游戏的主体控制部分了,这里是Game类:
这个类负责生成多少个Enemy,Role如何移动,游戏如何开始和如何结束,以及控制其他事务,比如记时类。
package{
import flash.display.Sprite;
import flash.events.*;
import flash.utils.Timer;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import fl.controls.Button;
import Config;
import Role;
import Enemy;
import TimeCount;
public class Game extends Sprite{
private var _role:Role;
private var _enemy:Sprite;
private var _time:TimeCount;
private var _enemyTotal:uint;
private var _gameFlag:Boolean;
private var startBtn:Button;
private var gameInfo:TextField;
//存储四个方向键是否按下的变量
private var leftArrow:Boolean=false;
private var rightArrow:Boolean=false;
private var upArrow:Boolean=false;
private var downArrow:Boolean=false;
public function Game():void{
startBtn = new Button();
startBtn.label = "StartGame";
startBtn.x=(Config.STAGE_W-startBtn.width)/2;
startBtn.y=(Config.STAGE_H-startBtn.height)/2;
addChild(startBtn);
startBtn.addEventListener(MouseEvent.MOUSE_DOWN,gameStart);
gameInfo = new TextField();
gameInfo.autoSize = TextFieldAutoSize.LEFT;
gameInfo.selectable = false;
gameInfo.text = "上下左右方向键控制方块移动,被黑球击中就挂了。";
gameInfo.x = (Config.STAGE_W-gameInfo.width)/2;
gameInfo.y = startBtn.y-30;
addChild(gameInfo);
}
private function gameStart(_evt:MouseEvent):void{
startBtn.removeEventListener(MouseEvent.MOUSE_DOWN,gameStart);
startBtn.visible = false;
gameInfo.visible = false;
init();
}
private function init():void{
initTime();
initRole();
initEnemy();
}
private function initTime():void{
_time = new TimeCount();
addChild(_time);
}
private function initRole():void{
_role = new Role();
addChild(_role);
_role.x=(Config.STAGE_W-_role.width)/2;
_role.y=(Config.STAGE_H-_role.height)/2;
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressedDown);
stage.addEventListener(KeyboardEvent.KEY_UP, keyPressedUp);
_role.addEventListener(Event.ENTER_FRAME, roleMove);
stage.addEventListener(Event.DEACTIVATE, missingStage);//Flash处于非活动状态时调用
}
private function initEnemy():void{
_enemyTotal = Config.ENEMY_TOTAL;
var t:Timer=new Timer(2000,1);
t.addEventListener(TimerEvent.TIMER, timeToCreateEnemy);
t.start();
}
private function createEnemy():void{
var enemy=new Enemy();
enemy.setRole(_role);
_enemy.addChild(enemy);
}
private function getEnemyNum():uint{
return this.numChildren;
}
private function timeToCreateEnemy(_evt:TimerEvent):void{
_enemy = new Sprite();
addChild(_enemy);
for (var i:uint=0; i<_enemytotal ; i++) {
createEnemy();
}
addEventListener(Event.ENTER_FRAME,checkFlag);
}
//按键时的检测,左上右下分别是37,38,39,40
private function keyPressedDown(_evt:KeyboardEvent) {
if (_evt.keyCode==37) {
leftArrow=true;
} else if (_evt.keyCode == 39) {
rightArrow=true;
} else if (_evt.keyCode == 38) {
upArrow=true;
} else if (_evt.keyCode == 40) {
downArrow=true;
}
}
//松开按键时
private function keyPressedUp(_evt:KeyboardEvent) {
if (_evt.keyCode==37) {
leftArrow=false;
} else if (_evt.keyCode == 39) {
rightArrow=false;
} else if (_evt.keyCode == 38) {
upArrow=false;
} else if (_evt.keyCode == 40) {
downArrow=false;
}
}
private function roleMove(_evt:Event) {
//移动的速度
var speed:Number=Config.ROLE_SPEED;
if (leftArrow) {
//MC的位置检测,下边相同
if (0<=_role.x) {
_role.x-=speed;
}
}
if (rightArrow) {
if (_role.x<=Config.STAGE_W-_role.width) {
_role.x+=speed;
}
}
if (upArrow) {
if (0<=_role.y) {
_role.y-=speed;
}
}
if (downArrow) {
if (_role.y<=Config.STAGE_H-_role.height*2) {
_role.y+=speed;
}
}
}
//Flash处于非激活状态时
private function missingStage(_evt:Event) {
leftArrow=false;
rightArrow=false;
upArrow=false;
downArrow=false;
}
private function checkFlag(_evt:Event):void{
//trace(_gameFlag);
if(_gameFlag){
_gameFlag = false;
gameOver();
}
}
private function gameOver(){
removeEventListener(Event.ENTER_FRAME,checkFlag);
_time.stopTime();
gameInfo.text = "You Failed At " + _time.getTime() + " Second!";
var format:TextFormat = new TextFormat();
format.font = "Arial";
format.color = 0xFF0000;
format.size = 30;
gameInfo.setTextFormat(format);
gameInfo.x=(Config.STAGE_W-gameInfo.textWidth)/2;
gameInfo.y=(Config.STAGE_H-gameInfo.textHeight)/2;
gameInfo.visible = true;
startBtn.visible = true;
startBtn.y = gameInfo.y + 40;
startBtn.addEventListener(MouseEvent.MOUSE_DOWN,gameStart);
//_role.roleClear();
_role.removeEventListener(Event.ENTER_FRAME, roleMove);
removeChild(_role);
while(_enemy.numChildren>0){
Enemy(_enemy.getChildAt(0)).enemyClear();
_enemy.removeChildAt(0);
}
removeChild(_enemy);
}
public function setGameFlag(_value:Boolean):void{
_gameFlag = _value;
}
}
}
GAME-Quake Live

Quake Live:http://www.quakelive.com
无比强大的Flash版本,游戏正规容量应当在800M左右的效果做在了10M左右的Flash里边,强大的地方,有兴趣的可以去参观一下。
GAME-NamePK 基于MD5的名字PK(下)
上接:“GAME-NamePK 基于MD5的名字PK(上)”
Source Code:
import org.nwhy.Player;
//这个是As3CoreLib里的工具类
import com.adobe.utils.StringUtil;
import gs.TweenLite;
btnStart.buttonMode=true;
btnStart.addEventListener (MouseEvent.CLICK,gameStart);
//开始游戏
function gameStart (_evt:MouseEvent) {
var p1Name:String=trimStr(inputP1.text);
var p2Name:String=trimStr(inputP2.text);
if (checkPName (p1Name) && checkPName (p2Name)) {
txtInfoShow.text="";
btnStart.buttonMode=false;
btnStart.removeEventListener (MouseEvent.CLICK,gameStart);
var p1=new Player(p1Name);
initPlayer(p1,mcP1);
var p2=new Player(p2Name);
initPlayer(p2,mcP2);
if(p1.getDex()>=p2.getDex()){
txtInfoShow.appendText("比对敏捷值 "+p1.getName()+" 获得优先攻击权 \n");
setTimeout(doPK,2000,p1,p2);
}else{
txtInfoShow.appendText("比对敏捷值 "+p2.getName()+" 获得优先攻击权 \n");
setTimeout(doPK,2000,p2,p1);
}
} else {
txtInfoShow.appendText("请输入名字!\n");
}
}
//初始化角色
function initPlayer(_player:Player,_mc:MovieClip){
_player.setBindMC(_mc);
setTxt(_mc.txtName,String(_player.getName()));
setTxt(_mc.txtHp,String(_player.getHp()));
setTxt(_mc.txtStr,String(_player.getStr()));
setTxt(_mc.txtDex,String(_player.getDex()));
TweenLite.to(_mc.hp,0.3,{width:_player.getHp()/5< <0});
TweenLite.to(_mc.str,0.3,{width:_player.getStr()*2});
TweenLite.to(_mc.dex,0.3,{width:_player.getDex()*2});
}
function doPK(_p1:Player,_p2:Player){
attack(_p1,_p2);
if(_p2.isAlive()){
setTimeout(doPK,1000,_p2,_p1);
}
}
//攻击函数
function attack(_p1:Player,_p2:Player){
var p1AttackValue:Array=_p1.attack();
gameInfoShow(txtInfoShow,p1AttackValue[1],p1AttackValue[0],_p1);
if(_p1.getRate()>_p2.getRate()){
_p2.setHp(_p2.getHp()-p1AttackValue[0]);
if(_p2.isAlive()){
setTxt(_p2.getBindMC().txtHp,String(_p2.getHp()));
TweenLite.to(_p2.getBindMC().hp,0.3,{width:_p2.getHp()/5< <0});
}else{
setTxt(_p2.getBindMC().txtHp,"死亡");
TweenLite.to(_p2.getBindMC().hp,0.3,{width:0});
txtInfoShow.appendText(_p2.getName()+" 被扁趴下 "+_p1.getName()+" 获得胜利 \n");
btnStart.buttonMode=true;
btnStart.addEventListener (MouseEvent.CLICK,gameStart);
}
}else{
txtInfoShow.appendText("但是 "+_p2.getName()+" 闪开了 \n");
}
}
//文本设置
function setTxt(_txt:TextField,_str:String){
_txt.text=_str;
}
//显示PK实况
function gameInfoShow(_txt:TextField,_style:String,_str:uint,_player:Player){
_txt.appendText(_player.getName()+" "+_style+" 伤害值为 "+_str+"\n");
}
//简单的见车名字输入
function checkPName (playerName:String):Boolean {
if (playerName=="") {
return false;
} else {
return true;
}
}
//去左右空格函数
function trimStr(_str:String){
return StringUtil.trim(_str);
}
相对来说主要就是攻击的顺序判断,攻击的命中,还有游戏是否结束的判断。
Source files here:http://dl.getdropbox.com/u/477487/flash/game/namePK.rar
GAME-What’s in your brain? 你的脑内是什么?
Demo:
想法来自http://usokonikki.com/,觉得挺好玩,加上正好想学一下JPGEncoder,就试着做了一下,呵呵,挺有趣的。
不过现实内容只做了9帧+空白,刚好对应数字0-9,也没怎么去研究usokonikki是怎么做的…哈。
无聊的人也试一下吧,看脑袋里装的是些什么~