function GameEvents(){
this._events = {};
}
GameEvents.prototype.addEvent = function(name , callback){
this._events[name] = this._events[name] || [];
this._events[name].push(callback);
}
GameEvents.prototype.removeEvent = function(name , callback){
if(!callback){
delete this._events[name];
}else{
this._events[name].forEach(
(c , index) => {
if(c === callback){
this._events[name].splice(index , 1);
}
}
)
}
}
GameEvents.prototype.emit = function(opts){
let name = opts['name'] || null;
let self = opts['self'] || null;
let args = opts['args'] || null;
name && this._events[name] && this._events[name].forEach(
item => {
item.apply(self , args);
}
)
}
GameEvents.prototype.hasEvent = function(name){
if(this._events[name]){
return true;
}else{
return false;
}
}
function GameSceen(){
this.bgColor = '#000';
this.components = new ComponentManage(this);
this.width = 0;
this.height = 0;
}
GameSceen.prototype.initScene = function(g){
this.width = g.width;
this.height = g.height;
this.parent = g;
this.components.reState();
}
GameSceen.prototype.init = function(self , options){
this.components.reState('' , options);
}
GameSceen.prototype.draw = function(){
background(this.bgColor);
this.components.draw(this);
}
function ComponentManage(_self , opts){
opts = opts || {};
this._components = {};
this._display = opts.display || 'display';
this._self = _self;
}
ComponentManage.prototype.hide = function(componentName){
if(this._components[componentName]){
this._components[componentName][this._display] = false;
}
return this;
}
ComponentManage.prototype.show = function(componentName){
if(this._components[componentName]){
this._components[componentName][this._display] = true;
}
return this;
}
ComponentManage.prototype.excludeShow = function(componentName){
for(var i in this._components){
if(i !== componentName){
this._components[i][this._display] = false;
}else{
this._components[i][this._display] = true;
}
}
return this;
}
ComponentManage.prototype.excludeHide = function(componentName){
for(var i in this._components){
if(i !== componentName){
this._components[i][this._display] = false;
}else{
this._components[i][this._display] = true;
}
}
return this;
}
ComponentManage.prototype.add = function(name , component , display){
component.parent = this._self;
display = display === undefined ? true : !!display;
this._components[name] = {
name: name,
component: component,
initState: {
display: display
}
};
this._components[name][this._display] = display;
return this;
}
ComponentManage.prototype.draw = function(context){
for(var i in this._components){
if(this._components[i][this._display]){
this._components[i].component.draw && this._components[i].component.draw(context);
}
}
}
ComponentManage.prototype.reState = function(name , options){
for(var i in this._components){
if(i == name || !name){
if(this._components[i].component.init && typeof this._components[i].component.init == 'function'){
this._components[i].component.init(this._components[i].component.parent , options)
}
this._components[i][this._display] = this._components[i].initState.display;
}
}
}
ComponentManage.prototype.get = function(name){
return this._components[name] ? this._components[name].component : null;
}
function Games(gameOptions){
this.width = gameOptions.width;
this.height = gameOptions.height;
this._state = '';
this.scenes = new ComponentManage(this);
this.currentFps = 0;
this.fps = gameOptions.fps || 50;
createCanvas(this.width, this.height);
frameRate(this.fps);
this.times = {
continued: 0,
time: 0
};
this._timer = null;
this.events = {
click: false,
dblClick: false,
keyIsPressed: false,
keyCode: null
}
}
Games.prototype.start = function(){
clearInterval(this._timer);
this._timer = setInterval(function(){
this.times.time += 1;
}.bind(this) , 1000);
}
Games.prototype.setScene = function(sceneName , options){
this._state = sceneName;
this.scenes.reState(sceneName , options);
}
Games.prototype.addScene = function(sceneName , scene , show){
if(scene['initScene'] && typeof scene['initScene'] === 'function'){
scene.initScene(this);
}
this.scenes.add(sceneName , scene);
if(show){
this._state = sceneName;
}
}
Games.prototype.draw = function(){
this.events.keyIsPressed = keyIsPressed;
this.events.keyCode = keyCode;
if(this.currentFps){
this.times.continued += round(1000 / this.currentFps , 2);
}
if(this._state){
this.scenes.excludeShow(this._state);
this.scenes.draw(this);
}
if(this.times.continued >= 1000){
this.times.continued = 0;
}
this.events = {
click: false,
dblClick: false,
keyIsPressed: false,
keyCode: null
}
}
function Menus(menuList , opts){
opts = opts || {};
this._opts = opts;
this.menuList = menuList;
this._events = new GameEvents();
this.init();
}
Menus.prototype.init = function(){
let opts = this._opts || {};
this.stroke = opts.stroke || '#fff';
this.textFill = opts.textFill || '#Fff';
this.radius = opts.radius || [15 , 0 , 15 , 0];
this.textSize = opts.textSize || 26;
this.baseY = opts.baseY || -60;
this.btnMinWidth = opts.btnMinWidth || 200;
this.btnMinHeight = opts.btnMinHeight || 40;
this.textPadding = opts.textPadding || 30;
this.btnMargin = opts.btnMargin || 20;
this.fill = opts.fill || [0,0,0];
}
Menus.prototype._click = function(menus , g){
if(g.parent.events.click){
let clickItem = null;
menus.forEach(
item => {
if(mouseX <= item.x + item.w && mouseX >= item.x && mouseY <= item.y + item.h && mouseY >= item.y){
clickItem = item;
}
}
)
if(clickItem && clickItem.action){
this._events.emit({
name: 'click',
args: [clickItem],
self: this
});
}
g.parent.events.click = false;
}
}
Menus.prototype._setHover = function(menus){
let hover = false;
menus.forEach(
item => {
if(mouseX <= item.x + item.w && mouseX >= item.x && mouseY <= item.y + item.h && mouseY >= item.y){
hover = true;
}
}
);
cursor(hover ? HAND : null);
}
Menus.prototype._drawBtn = function(item , x , y , w , h){
if(mouseX <= x + w && mouseX >= x && mouseY <= y + h && mouseY >= y){
fill(item.hover.bg);
stroke(item.hover.stroke);
}else{
fill(color(this.fill[0] , this.fill[1] , this.fill[2] , map(0.75 , 0 , 1 , 0 , 255)));
stroke(this.stroke);
}
let radius = item.radius || this.radius;
rect( x, y , w , h , radius[0] , radius[1] , radius[2] , radius[3]);
}
Menus.prototype.draw = function(g){
var baseY = g.height + this.baseY;
var menus = this.menuList;
for(var i = menus.length - 1; i >= 0; i--){
let item = menus[i];
textSize(item.textSize || this.textSize);
let textW = textWidth(item.name);
let btnWidth = textW + (this.textPadding * 2);
let btnHeight = (item.height || this.btnMinHeight);
let btnMinWidth = item.btnMinWidth || this.btnMinWidth;
btnWidth = btnWidth < btnMinWidth ? btnMinWidth : btnWidth;
this._drawBtn(
item,
g.width / 2 - (btnWidth / 2) ,
baseY - btnHeight ,
btnWidth ,
btnHeight
);
noStroke();
fill(item.textFill || this.textFill);
textAlign(LEFT , CENTER);
text(
item.name ,
g.width / 2 - (textW / 2) ,
baseY - (btnHeight),
btnWidth ,
btnHeight
);
menus[i].x = g.width / 2 - (btnWidth / 2) ;
menus[i].y = baseY - btnHeight;
menus[i].w = btnWidth;
menus[i].h = btnHeight;
baseY -= btnHeight + this.btnMargin;
}
this._setHover(menus);
this._click(menus , g);
}
Menus.prototype.onClick = function(clickCallback){
this._events.addEvent('click' , clickCallback);
}
var games = null;
function doubleClicked(){
games.events.dblClick = true;
}
function mouseClicked(){
games.events.click = true;
}
function index(games){
var indexPage = new GameSceen();
var indexBgComponent = {
foods: [],
draw: function(page){
let g = page.parent;
if(g.times.continued >= 1000){
this.foods.push({
width: floor(random(7 , 15)),
x: floor(random(10 , g.width - 10)),
y: 0,
speed: floor(random(1 , 5))
})
}
let foods = this.foods;
for(var i = foods.length - 1; i >= 0; i--){
foods[i].y += foods[i].speed;
circle(foods[i].x , foods[i].y , foods[i].width)
}
fill(color(0,0,0,map(0.25 , 0 , 1 , 0 , 255)));
rect(0,0,g.width , g.height);
},
init: function(){
this.foods = [];
}
}
var menus = new Menus([
{name: '开始游戏' , height: 60 , action: 'play' , hover: {
bg: 'green',
stroke: 'green',
}},
{
name: '游戏说明' , height: 40 , action: 'explain' ,
hover: {
bg: 'red',
stroke: 'red'
},
radius: [5,5,5,5],
textSize: 20
}
]);
menus.onClick(function(item){
if(item.action == 'play'){
this.parent.parent.setScene('GamePage');
}
if(item.action == 'explain'){
this.parent.components.hide('IndexMenu');
this.parent.components.show('Explain');
this.parent.components.show('CloseExplainButton');
}
})
var menus1 = new Menus([
{name: '我知道了' , height: 60 , action: 'yes' , hover: {
bg: 'green',
stroke: 'green',
}}
]);
menus1.onClick(function(item){
if(item.action == 'yes'){
this.parent.components.show('IndexMenu');
this.parent.components.hide('Explain');
this.parent.components.hide('CloseExplainButton');
}
})
indexPage.components.add('IndexBackground' , indexBgComponent);
indexPage.components.add('IndexMenu' , menus);
indexPage.components.add('Explain' , {
draw: function(page){
textAlign(LEFT , TOP);
noStroke();
fill('#fff');
textSize(14);
text([
'游戏玩法及规则如下:',
"1、使用键盘左右键控制小船的方向移动;",
"2、小船接到非红色食物时,加2秒游戏时长,并增加对应数字分数;",
"3、小船接到红色食物时,减少4秒游戏时长,并减少对应数字分数;",
"4、当游戏时长为0时,游戏结束;",
"5、游戏开始时,初始时长为20秒;"
].join('\n') , 100 , 100 , 200 , 100);
}
} , false);
indexPage.components.add('CloseExplainButton' , menus1 , false)
return indexPage;
}
function start(games){
var startPage = new GameSceen();
startPage.bgColor = 220;
startPage.components.add('Counts' , {
score: 0,
state: 'Ready',
init: function(){
this.time = 0;
this.state = 'Ready';
},
draw: function(page){
fill('blue');
noStroke();
textSize(30);
textAlign(CENTER , CENTER);
let t = this.score;
text(t, page.width - textWidth(t) - 20 , 0 , textWidth(t) , 40);
}
} , false)
startPage.components.add('Timer' , {
time: 20,
_startTime: 0,
_currentTime: 0,
draw: function(page){
let counts = this.parent.components.get('Counts');
if(counts.state == 'Play'){
this._currentTime = page.parent.times.time;
if(this._startTime == 0){
this._startTime = this._currentTime;
}
if(this.time - (this._currentTime - this._startTime) <= 0){
games.setScene('GameOverPage' , {
score: counts.score
});
}else{
fill('red');
noStroke();
textSize(30);
textAlign(CENTER , CENTER);
let t = this.time - (this._currentTime - this._startTime);
text(t, 20 , 0 , textWidth(t) , 40);
}
}
},
init: function(page){
this.time = 20;
this._currentTime = 0;
this._startTime = 0;
}
} , false)
function Box(){
this._events = new GameEvents();
this.init();
}
Box.prototype.init = function(page){
this.width = 60;
this.x = 0;
this.height = 40;
this.bottom = 10;
this.stroke = '#000';
this.fill = '#fff';
this.strokeWeight = 3;
this.score = 0;
if(page){
this.x = (page.width - this.width) / 2;
this.y = (page.height - this.bottom - this.height);
}
}
Box.prototype.draw = function(page){
let g = page.parent;
fill(this.fill);
stroke(this.stroke);
strokeWeight(this.strokeWeight);
beginShape();
vertex(this.x , page.height - (this.bottom + this.height));
vertex(this.x + 10 , page.height - (this.bottom));
vertex(this.x + (this.width - 10) , page.height - this.bottom);
vertex(this.x + this.width , page.height - (this.bottom + this.height));
endShape();
if(g.events.keyIsPressed){
if(g.events.keyCode == 37 || g.events.keyCode == 39){
this._events.emit({
name: 'keyDown',
args: [g.events.keyCode == 37 ? 'left' : 'right'],
self: this
})
}
}
}
Box.prototype.onKeyDown = function(callback){
this._events.addEvent('keyDown' , callback);
}
var box1 = new Box();
box1.onKeyDown(function(direction){
console.log(direction);
if(direction == 'left'){
if(this.x > 0){
this.x -= this.parent.width / 100;
}else{
this.x = 0;
}
}else{
if(this.x < (this.parent.width - this.width)){
this.x += this.parent.width / 100;
}else{
this.x = (this.parent.width - this.width)
}
}
})
startPage.components.add('Box' , box1 , false);
startPage.components.add('Food' , {
foods: [],
draw: function(page){
if(page.parent.times.continued >= 500){
this.foods.push({
type: floor(random(0,2)),
x: floor(random(10 , page.width - 10)),
y: 0,
speed: floor(random(2 , 8)),
size: floor(random(12 , 18))
})
page.parent.times.continued = 0;
}
let foods = this.foods;
let foodBox = this.parent.components.get('Box');
let timer = this.parent.components.get('Timer');
let counts = this.parent.components.get('Counts');
for(let i = foods.length - 1; i >= 0; i--){
let item = foods[i];
item.y += item.speed;
fill(item.type == 0 ? '#000' : 'red');
noStroke();
circle(item.x , item.y , item.size);
textSize(10);
fill('#fff');
textAlign(CENTER , CENTER);
text(item.size - 3 , item.x , item.y ,)
if(item.x >= foodBox.x && item.x < foodBox.x + foodBox.width){
if(item.y >= foodBox.y && item.y < foodBox.y + foodBox.height){
if(item.type == 0){
timer.time += 1;
counts.score += item.size - 3;
}else{
timer.time -= 5;
counts.score -= item.size - 3;
}
foods.splice(i , 1);
}
}
if(item.y > page.height){
foods.splice(i , 1);
}
}
},
init: function(){
this.foods = [];
}
} , false);
startPage.components.add('Ready' , {
startTime: 0,
time: 3,
draw: function(page){
let currentTime = page.parent.times.time;
let time = 3 - (currentTime - this.startTime);
noStroke();
fill('#000');
textSize(100);
textAlign(CENTER , CENTER);
if(time > 0){
text(time , 0 , 0 , page.width , page.height);
}else{
let counts = this.parent.components.get('Counts');
page.components.hide('Ready');
page.components.show('Box');
page.components.show('Food');
page.components.show('Counts');
page.components.show('Timer');
counts.state = 'Play';
}
},
init: function(page){
this.startTime = page.parent.times.time;
this.time = 3;
}
} , true);
return startPage;
}
function gameOver(games){
let gameOver = new GameSceen();
gameOver.components.add('Counts' , {
score: 0,
init: function(page , opts){
this.score = 0;
if(opts){
this.score = opts.score;
}
},
draw: function(page){
textSize(24);
fill('red');
noStroke();
textAlign(CENTER , CENTER)
text('游戏结束' , (page.width) / 2 , 200);
textSize(16);
fill('#fff');
noStroke();
textAlign(CENTER , CENTER)
text('得分' , (page.width) / 2, 230);
textSize(30);
fill('blue');
noStroke();
textAlign(CENTER , CENTER)
text(this.score , (page.width) / 2 , 260);
}
});
var menus = new Menus([
{name: '再来一次' , height: 60 , action: 'play' , hover: {
bg: 'green',
stroke: 'green',
}},
{
name: '返回首页' , height: 40 , action: 'backHome' ,
hover: {
bg: 'blue',
stroke: 'blue'
},
radius: [5,5,5,5],
textSize: 20
}
]);
menus.onClick(function(item){
if(item.action == 'play'){
this.parent.parent.setScene('GamePage');
}
if(item.action == 'backHome'){
this.parent.parent.setScene('IndexPage');
}
})
gameOver.components.add('Menu' , menus);
return gameOver;
}
function setup() {
games = new Games({
width: 600,
height: 600
});
games.addScene('IndexPage' , index(games) , true);
games.addScene('GamePage' , start(games) , false);
games.addScene('GameOverPage' , gameOver(games) , false);
games.start();
}
function draw() {
games.currentFps = frameRate();
games.draw();
}
* {
margin: 0px;
}
body{
padding: 10px;
}
canvas{
border: 1px solid red;
}
console