2D纸娃娃
在以前没有采用纸娃娃系统的游戏中,游戏角色的所有外观都是游戏
美工预先制作好的,因此数量有限,且占用较多资源(
内存,
硬盘空间),比如为了实现同一角色拿着不同武器奔跑的图像,开发者不得不绘制两套奔跑图,分别加上不同的武器。而实际上,变化的只是武器,这样多的一组奔跑图像就是多余的。而纸娃娃系统就是针对此设计的。
在纸娃娃系统中,角色的每个动作,每套服装,武器,都只有一套,而且是独立的。引擎可以根据不同的需要,临时将几组图像拼装到一起,进而形成变化丰富的角色外观。例如同时角色奔跑,可以在手部绘制武器A,也可以绘制武器B。
为了丰富角色的动作,2D纸娃娃有时也将角色的头,身,手,脚等部位的动作都分开绘制,在2D图像基础上展示了更多变的角色动作。
2D纸娃娃系统比起普通游戏的最大优势就在于,数量相同的角色外观下,占用更少的资源,角色的外观变化即时展示。而这样的系统在
编程稍稍复杂。
纸娃娃系统并不复杂,它的难度如以下这个问题。
桌子上有一个碗,碗里面有个杯子,移动了一下桌子,问杯子的坐标? (已知桌子的坐标,已知碗在桌子中的坐标)。
为了实现纸娃娃这种嵌套系统,我们需要引入相对坐标和绝对坐标的概念。
上回说的接口扮演者Act 和类演员Actor,我们添加新方法
/**获得绝对坐标X*/
public double getAbsoluteX();
/**获得绝对坐标Y*/
public double getAbsoluteY();
/**获得相对于容器坐标X*/
public double getX() ;
/**设置相对于容器坐标X*/
public void setX(double x);
/**获得相对于容器坐标Y*/
public double getY();
/**设置相对于容器坐标Y*/
public void setY(double y)
//获得绝对坐标方法
public double getAbsoluteX() {
//如果容器不为空, 绝对坐标X=容器的绝对坐标X加相对坐标X
return getActParent()== null ? getX() : getActParent().getAbsoluteX() + getX();
}
public double getAbsoluteY() {
//如果容器不为空, 绝对坐标Y=容器的绝对坐标Y加相对坐标Y
return getActParent() == null ? getY() : getActParent().getAbsoluteY() + getY();
}
绘制内部子扮演者方法
/** 绘制当前 */
public void draw(GameCanvas canvas, IAct thisAct) {
if (getModel() != null)
getModel().draw(canvas, thisAct);
drawChild(canvas);
};
//绘制子扮演者
public void drawChild(GameCanvas canvas) {
if (childList != null) {
Iterator<IAct> it = childList.iterator();
while (it.hasNext()) {
IAct act = it.next();
act.draw(canvas, act);
if (act.getScript() != null)
act.getScript().timeScript(act.getGameStage(), canvas, act);
}
}
}
package game.engines.inteface;
import java.util.HashMap;
import game.engines.Direction;
import game.engines.GameCanvas;
import game.engines.GameStage;
import game.engines.Model;
import game.engines.Script;
/**
* 扮演者
*/
public interface IAct {
/**将本身作为容器添加扮演者 被添加的扮演者绝对坐标将以容器相对坐标来计算*/
public void addAct(IAct act);
/**移除扮演者*/
public void removeAct(IAct act);
/**获得该扮演者上层容器*/
public IAct getActParent();
/**获得该扮演者上层容器*/
public void setActParent(IAct act);
/**绘制该版本扮演者
* 该方法同时
* @see drawChild
* */
public void draw(GameCanvas canvas,IAct act);
/**绘制扮演者容器中的子扮演者*/
public void drawChild(GameCanvas canvas);
/**设置扮演者脚本*/
public void setScript(Script script);
/**获得扮演者脚本*/
public Script getScript();
/**获得扮演者脚本*/
public HashMap<String,Object> getAttributes();
/**获得设置属性集*/
public void setAttributes(HashMap<String, Object> attributes);
/**获得设置属性*/
public void setAttributes(String key,Object value);
/**获得绝对坐标X*/
public double getAbsoluteX();
/**获得绝对坐标Y*/
public double getAbsoluteY();
/**获得相对于容器坐标X*/
public double getX() ;
/**设置相对于容器坐标X*/
public void setX(double x);
/**获得相对于容器坐标Y*/
public double getY();
/**设置相对于容器坐标Y*/
public void setY(double y);
/**获得宽度属性*/
public int getWidth();
/**设置宽度属性*/
public void setWidth(int width) ;
/**获得高度属性*/
public int getHeight();
/**设置高度属性*/
public void setHeight(int height);
/**获得移动速度*/
public double getMove();
/**设置移动速度*/
public void setMove(double move);
/**获得造型*/
public Model getModel();
/**设置造型*/
public void setModel(Model model);
/**设置方向*/
public void setDirection(Direction direction);
/**获得方向*/
public Direction getDirection();
/**获得所在舞台*/
public GameStage getGameStage();
/**设置所在舞台*/
public void setGameStage(GameStage stage);
}
利用纸娃娃系统来编写的太阳系。如图 太阳(黄色) 地球(青色) 月球(蓝色) 卫星(灰色)
如果将太阳系看作为一个整体你可以想象这用java需要多少行代码。
实际上只需要很少量的代码就足以完成它。
代码示例
package game.demo;
import android.graphics.Color;
import game.engines.Actor;
import game.engines.GameCanvas;
import game.engines.GameStage;
import game.engines.Model;
import game.engines.Script;
import game.engines.action.CircleMoveScript;
import game.engines.inteface.IAct;
/**
* 游戏剧情脚本
*/
public class MyGameScript extends Script {
/**
* 时间脚本
* */
public void timeScript(GameStage stage, GameCanvas canvas, IAct act) {
//新建一只精灵
Actor sun= new Actor();
//新建一个造型.
Model circle = new Model() {
@Override
public void draw(GameCanvas canvas, IAct thisAct) {
try {
//获得颜色属性
int color = (Integer) thisAct.getAttributes().get("Color");
canvas.getPaint().setColor(color);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// paint.setStyle(Paint.Style.STROKE);
//在绝对坐标上绘制一个圆
canvas.drawCircle(thisAct.getAbsoluteX(),
thisAct.getAbsoluteY(), thisAct.getHeight(),
canvas.getPaint());
}
};
//设置太阳的造型是圆
sun.setModel(circle);
//设置太阳的属性 Color 黄色
sun.setAttributes("Color", Color.YELLOW);
sun.setX(250);
sun.setY(150);
sun.setWidth(25);
sun.setHeight(25);
//设置脚本为圆运动 半径25 角度从100开始 速率10毫秒内0.005转
sun.setScript(new CircleMoveScript(25, 100, 0.005));
//新建地球
Actor earth = new Actor();
earth.setAttributes("Color", Color.CYAN);
earth.setModel(circle);
earth.setWidth(15);
earth.setHeight(15);
earth.setScript(new CircleMoveScript(130, 0, 0.01));
//
Actor moon = new Actor();
moon.setAttributes("Color", Color.BLUE);
moon.setModel(circle);
moon.setWidth(5);
moon.setHeight(5);
moon.setScript(new CircleMoveScript(35, 0, 0.04));
Actor star = new Actor();
star.setAttributes("Color", Color.GRAY);
star.setModel(circle);
star.setWidth(2);
star.setHeight(2);
star.setScript(new CircleMoveScript(10, 0, 0.1));
//将月亮和卫星组合一个系统
moon.addAct(star);
//将地球和月亮组合一个系统
earth.addAct(moon);
//将太阳和地球组合一个系统
sun.addAct(earth);
// 获得脚本的扮演者当前的舞台的场景 添加扮演者
act.getGameStage().getScreen().addAct(sun);
// 因为这个脚本只需要执行一次 所以执行完移除脚本
act.getGameStage().setScript(null);
}
package game.engines.action;
import game.engines.GameCanvas;
import game.engines.GameStage;
import game.engines.Script;
import game.engines.inteface.IAct;
/**
* 圆周运动
*/
public class CircleMoveScript extends Script {
double cx;
double cy;
double radius = 70;
double speed;
boolean init = false;
double step = 0;
public CircleMoveScript(double radius, double step,double speed) {
super();
this.radius = radius;
this.step = step;
this.speed=speed;
}
public void timeScript(GameStage stage,GameCanvas canvas,IAct a) {
//初始化圆心
if (!init) {
cx = a.getX();
cy = a.getY();
init = true;
}
double x;
double y;
//每次角度+速率
step += speed;
//圆的参数方程
x = cx + radius * Math.cos(step);
y = cy + radius * Math.sin(step);
a.setX(x);
a.setY(y);
}
}
*这时会发现Actor本身能代替Layer画布的功能,所以Layer可以删除今后由Actor任意实现多层嵌套纸娃娃系统的功能。
脚本引擎本身也是可以嵌套,重用性很强。我们的开发工作会越来越轻松。足够的灵活将带给我们所需要的强大的功能。
举个例子将来我们可以实现配置文件,
比如飞机坠落脚本,你能想象它的运动方式
飞机坠落.txt
[飞机]
x=0
y=0
width=100
height=100
script=圆周运动,向右运动,向下运动