开维游戏引擎(Kaiwei Engine)是基于js设计的跨平台游戏引擎。内核c++编写,v8引擎封装游戏函数,Assembly实现htm跨平台高效运行。下面以"飞机大战"小游戏为例,演示实现过程。

代码如下:
javascript
// 飞机大战小游戏
game.init()
game.setFPS(10);
var window = game.getWindow(); // 获取资源对象
var texture = game.getResource().getTexture("img/logo.png"); // 获取纹理数据对象
window.setIcon(texture); // 设置主游戏窗口图标
window.setTitle("开维游戏引擎-飞机大战小游戏"); // 设置主游戏窗口标题
// Audio音乐类,设置背景音乐
var audio = new Audio();
audio.playMusic("sound/bg.ogg"); // 播放背景音乐,循环播放
new AirplaneBattle();
game.run();
class Airplane {
// 飞机
sprite;
// 用户的飞机
userAirPlane;
// 子弹
bullets = [];
// 是否是敌方飞机
isEnemy;
heart = 3;
heartList = [];
// 速度
speed = 3;
// 子弹速度
bulletSpeed = 10;
// 创建子弹间隔
createBulletInterval = 2000;
// 创建子弹时间
createBulletTime = 0;
scene;
// 获取飞机对象
getSprite = function () {
return this.sprite;
}
// 获取子弹
getBullets = function () {
return this.bullets;
}
constructor(x, y, w, h, enemy,userAirPlane) {
this.scene = GlobalVariable.scene;
this.bullets = [];
this.isEnemy = enemy;
if (userAirPlane){
this.userAirPlane = userAirPlane;
}
let cache_ = game.getResource();
let texture;
if (enemy){
texture = cache_.getTexture("shmup_obj/enemy02.png");
}else {
texture = cache_.getTexture("shmup_obj/airplane_01_64x64.png");
this.heartUpdate()
}
const node = new Sprite();
node.setTexture(texture);
node.setSize(w, h);
node.setPosition(x, y);
node.setColor(1, 1, 1,1);
this.scene.addNode(node);
this.sprite = node;
// 创建子弹
this.createBullets(x, y);
// 更新事件
node.upDate(()=>{
if(GlobalVariable.gameOver){
return;
}
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
let x;
let y;
let sprite = this.sprite;
if (sprite) {
let pos = Util.getPosition(sprite);
x = pos.x;
y = pos.y;
}
// 创建子弹
let now = new Date().getTime();
let createBulletTime = this.createBulletTime;
let bulletInterval = this.createBulletInterval;
if (createBulletTime) {
// 敌人创建子弹
if (this.isEnemy) {
if (now - createBulletTime > bulletInterval && y <= h && sprite) {
this.createBullets(x, y);
}
} else {
// 创建子弹
if (now - createBulletTime > bulletInterval/3 && sprite) {
this.createBullets(x, y);
}
}
}
// 敌方飞机移动
if (this.isEnemy && this.sprite) {
this.sprite.setPosition(x, y + this.speed);
if (y > h + 10) {
this.resetPos();
}
}
// 子弹碰撞
this.bulletCrash();
// 飞机碰撞
this.crashAirplane();
// 子弹移动
let bullets = this.bullets;
if (bullets && bullets.length > 0) {
for (let i = 0; i < bullets.length; i++) {
let bullet = bullets[i];
let pos = Util.getPosition(bullet);
let bulletX = pos.x;
let bulletY = pos.y;
if (this.isEnemy) {
if(bulletY < h + 80 && bulletY > 0) {
bullet.setPosition(bulletX, bulletY + 2 * this.bulletSpeed);
}
} else {
if (bulletY > -80 && bulletY < h ){
bullet.setPosition(bulletX, bulletY - 4 * this.bulletSpeed);
}
}
}
}
if (GlobalVariable.gameOver) {
//this.gameEndLogic();
return;
}
})
}
// 血量变化
bloodChange(){
log(""+this.heart)
if (this.isEnemy){
return;
}
this.heart -= 1;
if (this.heart <= 0){
GlobalVariable.gameOver = true;
}
this.heartUpdate()
if (GlobalVariable.gameOver) {
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
Util.newText({
text: "游戏结束",
x: w/3 + 70,
y: 10,
width:90,
});
// 重新开始按钮
Util.newSprite({
x: w/2-73,
y: h/2-26,
width: 147,
height: 53,
texture: 'restart.png',
clickCb: ()=>{
new AirplaneBattle()
}
})
}
}
// 移除
remove = function () {
this.removeBullet();
this.sprite = null;
}
// 移除子弹
removeBullet() {
let bullets1 = this.bullets;
for (let bullets1Element of bullets1) {
bullets1Element.setHide(true)
}
this.bullets = [];
};
// 改变位置
resetPos() {
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
if (this.sprite) {
let x = Math.floor(Math.random() * (w - 100)) + 50;
this.sprite.setHide(false);
this.sprite.setPosition(x, -10);
}
};
// 创建子弹
createBullets(x, y) {
let cache_ = game.getResource();
if (!this.sprite) {
return;
}
let bulletPng;
if (this.isEnemy) {
bulletPng = cache_.getTexture("shmup_obj/bullet_01_32x32.png");
y = y + 60;
} else {
bulletPng = cache_.getTexture("shmup_obj/bullet_01_32x32_up.png");
y = y - 80;
}
let bullet = new Sprite();
bullet.setPosition(x, y);
bullet.setTexture(bulletPng);
bullet.setSize(64, 64);
bullet.setColor(1,1,1,1);
if(this.scene){
this.scene.addNode(bullet);
this.createBulletTime = new Date().getTime();
this.bullets.push(bullet);
}
}
// 飞机碰撞检测
crashAirplane() {
let enemy = this.isEnemy;
let userAirPlane = this.userAirPlane;
let sprite = this.sprite;
if (enemy && sprite) {
if(!userAirPlane){
return;
}
let userSprite = userAirPlane.getSprite();
// 飞机与飞机碰撞检测
if (this.crash(sprite, userSprite)) {
sprite.setHide(true);
this.resetPos();
userAirPlane.bloodChange()
return;
}
// 子弹碰撞敌方飞机检测
let bullets = userAirPlane.getBullets();
if (bullets && bullets.length > 0) {
for (let j = 0; j < bullets.length; j++) {
let bullet = bullets[j];
if (this.crash(bullet, sprite)) {
bullet.setHide(true);
sprite.setHide(true);
GlobalVariable.score += 1;
GlobalVariable.scoreText.setText("分数:"+GlobalVariable.score);
bullets.splice(j, 1);
this.resetPos()
return;
}
}
}
// 敌方子弹碰撞飞机检测
let bullets2 = this.bullets;
if (bullets2 && bullets2.length > 0) {
for (let j = 0; j < bullets2.length; j++) {
let enemyBullet = bullets2[j];
if (this.crash(enemyBullet, userSprite)) {
bullets2.splice(j, 1);
enemyBullet.setHide(true);
userAirPlane.bloodChange()
return;
}
}
}
}
}
// 子弹碰撞检测
bulletCrash() {
let enemy = this.isEnemy;
let userAirPlane = this.userAirPlane;
let sprite = this.sprite;
if (enemy && sprite) {
if(!userAirPlane){
return;
}
let bullets = userAirPlane.getBullets();
let bullets2 = this.bullets;
if (bullets2 && bullets2.length > 0) {
for (let i = 0; i < bullets2.length; i++) {
let enemyBullet = bullets2[i];
if (bullets && bullets.length > 0) {
for (let j = 0; j < bullets.length; j++) {
let bullet = bullets[j];
if (this.crash(bullet, enemyBullet)) {
bullet.setHide(true);
enemyBullet.setHide(true);
bullets2.splice(i, 1);
i--;
bullets.splice(j, 1);
j--;
break;
}
}
}
}
}
}
}
// 碰撞检测
crash(o1, o2) {
if (o1 && o2) {
if (Physics.rectRect(Util.getPosition(o1), Util.getPosition(o2))) {
audio.playSound("sound/1.wav"); // 循环音效,例如按钮点击声、脚步声、爆炸声、技能音效
return true;
}
}
return false;
}
// 创建血量
createHeart(x, y,full){
const cache_ = game.getResource();
let img;
if (full){
img = cache_.getTexture("full-heart.png");
}else {
img = cache_.getTexture("empty-heart.png");
}
const heart = new Sprite();
// log("sprite start")
// ObjUtil.printAllObjectMethods(heart);
heart.setTexture(img);
heart.setPosition(x, y);
heart.setSize(24, 22);
GlobalVariable.scene.addNode(heart);
return heart;
}
// 血量
heartUpdate(){
let list = this.heartList;
if(list && list.length === 0){
for (let i = 0; i < this.heart; i++) {
list.push(this.createHeart(10 + i * 32, 5,true));
}
}else {
const cache_ = game.getResource();
for (let i = 0; i < list.length; i++) {
const h = list[i];
let img;
if (i < this.heart){
img = cache_.getTexture("full-heart.png");
}else {
img = cache_.getTexture("empty-heart.png");
}
h.setTexture(img);
}
}
}
}
class AirplaneBattle{
// 敌方飞机
enemys = [];
// 主角飞机
heroAirplane = null;
constructor() {
this.init()
}
init(){
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
GlobalVariable.gameOver = false;
Util.bj({picture: "mainbg.png"});
this.heroAirplane = this.createHeroAirplane()
this.createEnemy();
// 分数
GlobalVariable.score = 0;
GlobalVariable.scoreText = Util.newText({
x: w - 120,
y: 10,
text: "分数:"+GlobalVariable.score+"",
width:300
})
// 更新事件
GlobalVariable.scoreText.upDate(()=> {
if (GlobalVariable.gameOver) {
return;
}
// 难度升级
if(GlobalVariable.score > 5 && this.enemys.length == 1){
this.createEnemy();
}
})
game.setKeyCallBack((key,action)=>{
log(key+" "+action);
let type = "";
if (key == GlobalVariable.KEY_W || key == GlobalVariable.KEY_UP){
type = "up";
}
if (key == GlobalVariable.KEY_S || key == GlobalVariable.KEY_BOTTOM){
type = "down";
}
if (key == GlobalVariable.KEY_A || key == GlobalVariable.KEY_LEFT){
type = "left";
}
if (key == GlobalVariable.KEY_D || key == GlobalVariable.KEY_RIGHT){
type = "right";
}
this.changePos(type);
});
}
changePos(type){
let airplane = this.heroAirplane;
if (airplane == null) {
return;
}
let sprite = airplane.getSprite();
let position = Util.getPosition(sprite);
switch (type){
case "up":
position.y -= 10;
break;
case "down":
position.y += 10;
break;
case "left":
position.x -= 10;
break;
case "right":
position.x += 10;
break;
}
sprite.setPosition(position.x,position.y);
}
getHeroAirplane() {
return this.heroAirplane;
}
// 创建飞机
createHeroAirplane() {
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
let myAirplaneX = (w / 2)-50;
let myAirplaneY = h - 80;
return new Airplane(myAirplaneX,myAirplaneY,64,64,false);
}
// 创建敌方飞机
createEnemy(){
let w = game.getWindow().getWidth();
const x = Math.floor(Math.random() * (w - 100)) + 50;
const enemyAirplane = new Airplane(x,0,64,64,true,this.heroAirplane);
this.enemys.push(enemyAirplane);
}
}
// 工具类:生成对象
class Util{
// 函数功能:创建背景场景,并返回场景和背景节点
static bj=(options={})=>{
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
let config = {
x: 0,
y: 0,
width: w,
height: h,
picture: "bg.jpg",
...options
};
// 创建场景
let scene = new Scene();
GlobalVariable.scene = scene;
game.pushScene(scene);
// 添加游戏背景图
const cache_res = game.getResource();
let bg = cache_res.getTexture(config.picture);
const node = new Sprite();
node.setTexture(bg);
node.setSize(config.width,config.height);
node.setPosition(config.x,config.y);
node.setColor(1,1,1,1);
scene.addNode(node);
// 返回场景对象
return {scene:scene,backgroundNode:node};
}
// 函数功能:创建精灵节点,并返回精灵
static newSprite(options={}){
let config = {
x: 0,
y: 0,
width: 50,
height: 30,
clickCb: undefined,
texture: "",
...options
};
if(!GlobalVariable.scene){
// log("scene is not exist");
return;
}
const cache_ = game.getResource();
let bg = cache_.getTexture(config.texture);
let sprite = new Sprite();
sprite.setTexture(bg);
sprite.setSize(config.width, config.height);
sprite.setPosition(config.x, config.y);
GlobalVariable.scene.addNode(sprite);
sprite.click(()=>{
if (config.clickCb !== undefined && config.clickCb instanceof Function){
config.clickCb();
}
});
return sprite;
}
// 函数功能:创建文本节点,并返回文本对象
static newText(options){
// 如果场景不存在,返回
if(!GlobalVariable.scene){
return;
}
// 文本节点参数
let config = {
x: 0,
y: 0,
width: 50,
height: 30,
text: "",
fontSize: 20,
textColor: [1,0,0],
...options
};
// 打印日志,调试用
log(JSON.stringify(config));
// 设置lab标签
const lab = new Label();
lab.setPosition(config.x, config.y);
lab.setSize(config.width, config.height);
lab.setFont("font/st.ttf", config.fontSize);
lab.setText(config.text);
// 设置颜色
if(config.textColor !== undefined && config.textColor.length === 3){
let configColor = config.textColor;
lab.setTextColor(configColor[0],configColor[1],configColor[2],1);
}else {
lab.setTextColor(1,0,0,1);
}
lab.setColor(1,1,1,0);
GlobalVariable.scene.addNode(lab);
return lab;
}
// 函数功能:获取节点位置和大小
static getPosition(node){
if (!node){
return;
}
let x = node.getPosition().x;
let y = node.getPosition().y;
let width = node.getSize().x;
let height = node.getSize().y;
return {x:x, y:y, width:width, height:height};
}
}
class Physics {
// 碰撞检测算法: 矩形 vs 矩形
static rectRect(rect1, rect2,rate1 = 1,rate2=1) {
if(rate1!==1){
rect1.x += (rate1-1)*rect1.width;
rect1.y += (rate1-1)*rect1.height;
rect1.width *= rate1;
rect1.height *= rate1;
}
if(rate2!==1){
rect2.x += (rate2-1)*rect2.width;
rect2.y += (rate2-1)*rect2.height;
rect2.width *= rate2;
rect2.height *= rate2;
}
let b = rect1.x < rect2.x + rect2.width
&& rect1.x + rect1.width > rect2.x
&& rect1.y < rect2.y + rect2.height
&& rect1.y + rect1.height > rect2.y;
return b;
}
// 碰撞检测算法: 圆形 vs 圆形
static circleCircle(circle1, circle2) {
const dx = circle1.x - circle2.x;
const dy = circle1.y - circle2.y;
const distance = Math.sqrt(dx * dx + dy * dy);
return distance < circle1.radius + circle2.radius;
}
// 碰撞检测算法: 矩形 vs 圆形
static rectCircle(rect, circle) {
let testX = circle.x;
let testY = circle.y;
if (circle.x < rect.x) testX = rect.x; else if (circle.x > rect.x + rect.width) testX = rect.x + rect.width;
if (circle.y < rect.y) testY = rect.y; else if (circle.y > rect.y + rect.height) testY = rect.y + rect.height;
const distX = circle.x - testX;
const distY = circle.y - testY;
const distance = Math.sqrt(distX * distX + distY * distY);
return distance <= circle.radius;
}
}
// 全局类,全局变量和参数
class GlobalVariable{
// 键盘键值
static KEY_W = 87;
static KEY_S = 83;
static KEY_A = 65;
static KEY_D = 68;
static KEY_BOTTOM = 40;
static KEY_UP = 38;
static KEY_LEFT = 37;
static KEY_RIGHT = 39;
static gameOver;
// 场景
static scene;
// 分数
static score = 0;
// 分数游戏对象
static scoreText;
}
代码功能:
- main.js:游戏初始化,设置背景音乐等
- util.js:公共类,设置文字等
- Airplane.js:飞机子弹碰撞
- AirplaneBattle.js:飞机创建
- GlobalVariable.js:键盘码值
- physics.js:碰撞算法
开维游戏引擎代码简单,函数精简,尽量用极少的js脚本实现游戏功能。代码跨平台通用,一次编写,多端运行,可以直接导出生成exe或者html目录直接运行:

开维游戏引擎下载: www.ikaiwei.com/download/ga...
飞机大战页面演示: www.ikaiwei.com/gamejs/exam...