最近去看了看JavaScript,语法啥的比较简单,加上能页面显示,那写个小游戏就无可厚非了。
js新手,第一次写,主要还是学习吧。
首先写个简单的页面文件
<!DOCTYPE html>
<html lang="en">
<head>
<script src="game.js"></script>
<link rel="stylesheet" href="./css/game.css" />
<meta charset="UTF-8">
<title>game</title>
</head>
<body>
<canvas id="canvas" >
</canvas>
<button onclick="begin()">begin</button><br>
</div>
</body>
</html>
写个css:
#canvas {
width: 800px;
height: 800px;
position: relative;
background: yellow;
}
开始主要的game.js
首先是获取画布,然后写个刷新画布的函数,包括一个分数和生命的显示函数
js中虽有数据类型,但变量一律用var来声明(不写也行),且均为全局(哪怕在函数中声明),想要单独的局部变量(比如for里的i),可以用let声明)
函数使用function声明
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var reflshflag=0;
function refresh(){
//刷新屏幕
if(reflshflag>4){
canvas.height=canvas.height;
reflshflag=0;
paintText();
}else{reflshflag++}
}
function paintText(){
context.font = "bold 10px 微软雅黑";
context.fillText("SCORE:" + score,20,20);
context.fillText("LIFE:" + hero.life,20,130);
context.fillText("BIGBONG:" + hero.bigbongtime,20,140);
}
js中存在一个叫“变量提升”的机制,在启动时会把所有声明提升到最前,所以哪怕在后面声明的函数与变量,也可以在前面被引用(与c、java等不同)
var speed=2;
var MaxEnemy=30;
var nowEnemy=0;
var state=1;//用于切换游戏状态
var EnemyWhite={EnemyLevel:5,speed:1,life:5,color:"White"};
var EnemyGreen={EnemyLevel:1,speed:2,life:1,color:"green"};
var EnemyBlue={EnemyLevel:3,speed:3,life:2,color:"blue"};
var EnemyRed={EnemyLevel:3,speed:6,life:1,color:"red"};
var score=0;
定义一些变量,
JS中的对象类型,十分类似py中的字典,以name:value的方式写,且通过EnemyBlue.life的方式获取
函数中也能有变量和函数,可以当作类来用
下面创建我方飞机类
// 我方飞机的构造函数
function Hero(){
this.imgs = new Image(20,20);
this.imgs.src ="you.jpg";//图片地址
this.imgs.width=20;
this.imgs.height=20;
this.length = 15;
this.width = 15;
this.height = 15;
this.bigbongtime=6;
this.bulletflag=0;
// 定义飞机的坐标
this.x = 120;
this.y = 130;
this.life=3;
// 绘制方法
this.paint = function(){
context.drawImage(this.imgs,this.x,this.y);
}
this.bigbongflag = 0;
//内置射击cd计时器
this.shoot = function(){
this.bulletflag++;
this.bigbongflag ++;
}
this.bang = function(){
this.life --;
}
}
创建一个hero,同时获取页面中的body,为其加上按键捕捉事件keydown,为了能及时响应,所以还得加上一个监听器去监听事件
addEventListener(‘keydown’,function (event){
-------------------
}
var hero = new Hero();
var body = document.getElementsByTagName('body')[0];
body.addEventListener('keydown',function (event){
switch(event.keyCode){
case 65:
case 37://左
hero.x-=5*speed;
break;
case 87:
case 38: //上
hero.y-=5*speed;
break;
case 68:
case 39: //右
hero.x+=5*speed;
break;
case 83:
case 40: //下
hero.y+=5*speed;
break;
case 74:
if(bulletflag >=33){
bulletflag=0;
bullets.push(new Bullet())};
break;
case 73:
if(hero.bigbongflag >=50){
hero.bigbongflag=0;
bigbong();}
break;
}
//console.log("move "+i+" in "+speed );
},false)
以上用于检测按键,wasd控制移动,j发射子弹,i使用炸弹
接下来生成敌人
这里有两个参数,敌人的配置,创建的地址,配置就是上面写的那些
function Enemy(config,createaddress){
this.EnemyLevel = config.EnemyLevel;
this.speed = config.speed;
this.life = config.life;
this.img = new Image(20,20);
this.img.src =config.color+".jpg";
this.img.maxwidth=20;
this.img.height=20;
// 坐标
this.x = createaddress;
this.y = 0;
// 绘制
this.paint = function(){
context.drawImage(this.img,this.x,this.y);
}
// 运动
this.step = function(){
this.y +=0.1*this.speed;
}
//碰撞检测,可能是子弹,可能是我方飞机
this.checkHit = function(zd){
return zd.y + zd.height > this.y
&& zd.x + zd.width > this.x
&& zd.y < this.y + 20
&& zd.x < this.x + 20
}
// 撞击的方法
this.bang = function(){
this.life -- ;
}
}
有了构造函数就可以写个创建函数了
使用数组来存储对象,这里面数组类似list,使用push向最后加入元素,使用.splice(i,n)删除从第i个位置开始的n个元素
随机数生成Math.floor(Math.random()*100);
随机生成不同等级的敌人
var enemies = [];
var enemyflag=0;//敌人生成的cd0.33秒一个
function createEnemy() {
enemyflag++;
if(enemyflag % 33 ==0){
if(nowEnemy<=MaxEnemy){
nowEnemy+=1;
let createAddress = Math.floor(Math.random()*800);
let enemytype = Math.floor(Math.random() * 100);
if(enemytype < 10){
enemies.push(new Enemy(EnemyWhite,createAddress));
}else if(enemytype >= 10 && enemytype <35){
enemies.push(new Enemy(EnemyGreen,createAddress));
}else if(enemytype >= 35 && enemytype <70){
enemies.push(new Enemy(EnemyBlue,createAddress));
}else if(enemytype >= 70 && enemytype <100){
enemies.push(new Enemy(EnemyRed,createAddress));
}
}
}
}
由于敌人数量众多,我们为了让敌人移动,还得专门写一个对所有敌人操作的函数
通过数组的length获得数组长度
function Enemiesdo(){
for(var i = 0;i < enemies.length;i++){
enemies[i].paint();// 敌人绘制函数
enemies[i].step();// 敌人运动
// 敌人死亡,两种情况
if(enemies[i].y > 140){
enemies.splice(i,1);
nowEnemy--;
}
if(enemies[i].life<=0){
score=enemies[i].EnemyLevel+score;
enemies.splice(i,1);
nowEnemy--;
}
//碰撞检测
if(enemies[i].checkHit(hero)){
score+=enemies[i].EnemyLevel;
enemies.splice(i,1);
nowEnemy--;
hero.bang();
if(hero.life<=0) {state=2;paintText();}
}
// 子弹撞
for(var j = 0;j < bullets.length;j++){
if(enemies[i].checkHit(bullets[j])){
enemies[i].bang();
bullets[j].bang();
}
}
}
}
接下来是子弹的类,基本原理相同
我这里只有一种子弹,所以没写更多东西,如果考虑拥有多种弹药,可以如同敌人函数一般使用config读取不同伤害、射速、图标的子弹,再在hero中加个当前子弹类型的flag,用于切换
function Bullet(){
//多种子弹function Bullet(config,typeflag)
this.str = new Image(5,5);this.str.src="pong.jpg";
this.str.width=5;
this.str.height=5;
this.width = 10;
this.height = 10;
// 坐标
this.x = hero.x + hero.width/2 - this.width/2;
this.y = hero.y - this.height;
// 绘制
this.paint = function(){
context.drawImage(this.str,this.x,this.y);
}
// 运动
this.step = function(){
this.y -=0.2;
}
this.life=1;
// 撞击的方法,用于修改子弹是否碰撞的属性
this.bang = function(){
this.life--;
}
}
同样用一个数组来存储子弹,并用一个统一的函数来对子弹进行操作
//存储子弹
var bullets = [];
function bulletsdo(){
for(var i = 0;i < bullets.length;i++){
bullets[i].paint();// 绘制所有子弹
bullets[i].step();// 子弹运动
//子弹删除
if(bullets[i].y < -bullets[i].height || bullets[i].life<=0){
bullets.splice(i,1);
}
}
}
自此,飞机大战的要素,飞机-敌人-子弹都有了
开始游戏
通过setInterval来进行(该函数会定时循环运行)
//进程主循环
function begin(){
setInterval(function(){
switch (state) {
case 1:
reflsh();
hero.paint();
hero.shoot();
bulletsdo();
createEnemy();
Enemiesdo();
break;
case 2:
break;
}
},10);
}
//每10毫秒运行一次,相当于1秒100帧
运行页面,开始游戏吧!
(飞机图片自己找了放到目录里,我随便截到图,都没扣背景)
大概就这个样子吧
有时间再写个贪吃蛇