开维游戏引擎(Kaiwei Engine)是基于js设计的跨平台游戏引擎。内核c++编写,v8引擎封装游戏函数,Assembly实现htm跨平台高效运行。下面以“贪吃蛇”小游戏为实例,演示实现过程。
下载源码,打开gmp工程后,运行即可出现界面:
代码如下:
// 贪吃蛇游戏
// 初始化游戏
game.initSize(800,800) // 设置分辨率
game.setFPS(10); // 设置帧率
// 设置窗口log和标题
var texture = game.getResource().getTexture("logo.png"); // 另一种获取纹理数据对象
var window = game.getWindow(); //获取游戏窗口
window.setIcon(texture); // 设置窗口图标
window.setTitle("贪吃蛇小游戏");
// 申请游戏类
new Snake();
// 设置键盘回调函数
game.setKeyCallBack((key,action)=>{
if ((key == Snake.KEY_W || key == Snake.KEY_UP) && Snake.type != "down"){
Snake.type = "up";
}
else if ((key == Snake.KEY_S || key == Snake.KEY_BOTTOM) && Snake.type != "up"){
Snake.type = "down";
}
else if ((key == Snake.KEY_A || key == Snake.KEY_LEFT) && Snake.type != "right"){
Snake.type = "left";
}
else if ((key == Snake.KEY_D || key == Snake.KEY_RIGHT) && Snake.type != "left"){
Snake.type = "right";
}
//log("key "+key+" action "+action+" type "+Snake.type);
});
// 游戏运行
game.run();
class Snake{
// 移动距离
static stepSize = 34;
static updateTime = new Date().getTime();
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 refreshInterval=200;
// 默认移动方向
static type = "right";
static gameOver = false;
static score = 0;
static score_text;
static snake_ = [];
static jp_button;
static cache_ = game.getResource();
static scene;
constructor(){
Snake.init();
}
// 函数功能:初始化界面
static init(){
this.scene = Util.bj();
this.createSnakeButton(100,100)
this.createJp(200,200)
let w = game.getWindow().getWidth();
this.score_text = Util.newText(w-50,20,""+this.score,30,20);
// 重新开始按钮
Util.newSprite({
x: 10,
y: 5,
width: 80,
height: 40,
texture: 'zaicitiaozhan.png',
clickCb: ()=>{
Snake.restart();
}
})
this.scene.upDate((time)=>{
let s;
if(this.snake_ && !this.gameOver){
if(this.gameOver){
this.gameEndLogic();
return;
}
const etime = new Date().getTime();
const st = etime - this.updateTime;
let number = this.refreshInterval - 10 * this.score <= 100 ? 100 : this.refreshInterval - 10 * this.score;
if(st >= number){
this.updateTime = etime;
const oldPositions = [];
for(let i = 0; i < this.snake_.length; i++){
s = this.snake_[i];
if(s){
oldPositions.push(Util.getPosition(s));
}
}
this.changePosition(this.snake_[0],this.type);
if(this.gameOver){
Util.newText(w/2-50,20,"游戏结束",85,30);
return;
}else{
// 替换位置
for(let i = 1; i<this.snake_.length && this.snake_.length > 1 ; i++){
s = this.snake_[i];
if(s){
const img = this.cache_.getTexture("leibg.png");
const pos = oldPositions[i - 1];
s.setTexture(img);
s.setPosition(pos.x, pos.y);
}
}
}
if(this.score_text){
this.score_text.setText(""+this.score);
}
}
}
});
}
// 函数功能:重新开始
static restart(){
this.gameOver = false;
this.score = 0;
this.snake_ = [];
this.init();
}
static createSnakeButton(x,y,unshift){
let button_ = Util.newSprite({
x,y,
texture:"lei0.png",
width:34,
height: 34,
});
if(unshift){
this.snake_.unshift(button_)
}else{
this.snake_.push(button_)
}
}
static createJp(x,y){
let jp_button = Util.newSprite({
x,y,
texture:"phbjp.png",
width:34,
height: 34,
});
this.jp_button = jp_button;
}
static changePosition(node,direction){
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
let position = Util.getPosition(node);
let x = position.x;
let y = position.y;
//log("direction "+direction+" x "+x+" y "+y+ " w "+w +" h "+h);
if(direction){
let stepSize1 = this.stepSize;
if(direction == "left"){
x = x - stepSize1;
if(x < -stepSize1){
x = w + x;
}
}
if(direction == "right"){
x = x + stepSize1;
if(x > w){
x = w - x;
}
}
if(direction == "up"){
y = y - stepSize1;
if(y < -stepSize1){
y = h + y;
}
}
if(direction == "down"){
y = y + stepSize1;
if(y > h){
y = y - h;
}
}
log(" x "+x+" y "+y);
let newPosition = {x,y,width:position.width,height:position.height};
const jpPosition = Util.getPosition(this.jp_button);
// 吃奖牌
if(Physics.rectRect(jpPosition,newPosition)){
this.score = this.score + 1;
let number = 50;
const jp_x = Math.floor(Math.random() * w + number);
const jp_y = Math.floor(Math.random() * h + number);
this.jp_button.setPosition(jp_x > w- number?w-number : jp_x,jp_y>h-number?h-number:jp_y );
this.createSnakeButton(x,y,true)
}else{
for(let i = 1; i < this.snake_.length; i++){
const s = this.snake_[i];
if(s){
// 头与身体碰撞
if(Physics.rectRect(Util.getPosition(s),newPosition)){
this.gameOver = true;
break;
}
}
}
if(!this.gameOver){
node.setPosition(x, y);
}
}
}
}
}
class Util{
static scene;
static bj=()=>{
let scene = new Scene();
this.scene = scene;
game.pushScene(scene);
const cache_ = game.getResource();
let w = game.getWindow().getWidth();
let h = game.getWindow().getHeight();
let bg = cache_.getTexture("mainbg.png");
const node = new Sprite();
node.setTexture(bg);
node.setSize(w,h);
node.setPosition(0,0);
node.setColor(1,1,1,1);
scene.addNode(node);
return scene;
}
static newSprite(options={}){
let config = {
x: 0,
y: 0,
width: 50,
height: 30,
clickCb: undefined,
texture: "",
...options
};
if(!this.scene){
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);
this.scene.addNode(sprite);
sprite.click(()=>{
if (config.clickCb !== undefined && config.clickCb instanceof Function){
config.clickCb();
}
});
return sprite;
}
// 函数功能:设置字体和颜色
static newText(x,y,text,width=50,height=30){
if(!this.scene){
return;
}
const lab = new Label();
lab.setPosition(x, y);
lab.setSize(width, height);
lab.setFont("st.ttf", 20);
lab.setText(text);
lab.setTextColor(1.0,0.5,0.2,1.0); // 设置字体颜色
lab.setColor(0,0,0,0); // 标签背景颜色为黑色并透明
this.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) {
return rect1.x < rect2.x + rect2.width
&& rect1.x + rect1.width > rect2.x
&& rect1.y < rect2.y + rect2.height
&& rect1.y + rect1.height > rect2.y;
}
// 碰撞检测算法: 圆形 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;
}
}
代码功能:
- main.js:游戏初始化,设置背景音乐等
- util.js:创建场景,设置文字等公用类
- snake.js:贪吃蛇移动代码
- physics.js:碰撞算法
开维游戏引擎代码简单,函数精简,尽量用极少的js脚本实现游戏功能。代码跨平台通用,一次编写,多端运行,可以直接导出生成exe或者html目录直接运行:
开维游戏引擎下载: www.ikaiwei.com/download/ga…
贪吃蛇小游戏html版页面演示: www.ikaiwei.com/gamejs/exam…
源码下载: github.com/ctrljshaha/…