注册

520和女朋友搞点不一样的礼物, html+css+js做一个网页版坦克大战游戏

坦克游戏玩法及介绍

我们先来看一下首页。

247ddc089018958bde2a3f319a95106f.png

打开这个首页很简单,基本是上面这个样子,然后选择两个人回车就可以进行玩耍了,这个游戏需要两个人一起操作,玩家1(我): 使用WASD四个键进行上左下右方向的控制,通过space键进行设计射击。玩家2(女朋友):通过方向键上下左右控制方向,通过enter键盘射击。基本上我控制整个电脑键盘的左边,她控制电脑键盘的右边。通过N键进行下一关,P键选择上一关。再键盘上显示如下。
f1ecc7157ceb0d6f24cf83b99545a51b.png

演示如何进入游戏

3f1f477701f509a123ca9971c9ae94ef.png

通过方向键的下键选择两个人,然后点击回车进入游戏。也可以选择一个人进行回车进行战斗。

一个人战斗的状态。

af4760cce23cba5570cbb0816aaa8b3a.png

两个人战斗的状态。

ccdae9e22ec3ebb725ba33fa4ccd3435.png

比如图中红色标记的砖头是打不破的,只能绕道走,还有只能再yellow标记的区域内操作,其它砖头用子弹就可以打破,不能让对手先打破你的大本营(我右边中间的老鹰),不然又得GG.

整个游戏规则大体是这样,下面看一下代码。

项目结构

37e37e92d7b9c0ec2a931ef3e3210581.png

整个项目由五部分组成,分为背景音乐、基础样式、动图、核心JS及首页静态展示。不涉及后端,纯前端实现。

index.html

<!DOCTYPE html>
<html lang="zh" class="no-js demo-1">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="js/jquery.min.js"></script>
<script src="js/Helper.js"></script>
<script src="js/keyboard.js"></script>
<script src="js/const.js"></script>
<script src="js/level.js"></script>
<script src="js/crackAnimation.js"></script>
<script src="js/prop.js"></script>
<script src="js/bullet.js"></script>
<script src="js/tank.js"></script>
<script src="js/num.js"></script>
<script src="js/menu.js"></script>
<script src="js/map.js"></script>
<script src="js/Collision.js"></script>
<script src="js/stage.js"></script>
<script src="js/main.js"></script>
<link rel="stylesheet" type="text/css" href="css/default.css" />
<style type="text/css">
#canvasDiv canvas{
position:absolute;
}
</style>
</head>
<body>
<div class="container">
<head><h3>操作说明:玩家1:WASD上左下右,space射击;玩家2:方向键,enter射击。n下一关,p上一关。</h3></head>
<div class="main clearfix">
<div id="canvasDiv" >
<canvas id="wallCanvas" ></canvas>
<canvas id="tankCanvas" ></canvas>
<canvas id="grassCanvas" ></canvas>
<canvas id="overCanvas" ></canvas>
<canvas id="stageCanvas" ></canvas>
</div>
</div>

</div><!-- /container -->
<div style="text-align:center;">
<p>来源:<a href="https://sunmenglei.blog.csdn.net/" target="_blank">孙叫兽的博客</a></p>
</div>

</body>
</html>

css

*, *:after, *:before { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
body, html { font-size: 100%; padding: 0; margin: 0; height: 100%;}

/* Clearfix hack by Nicolas Gallagher: http://nicolasgallagher.com/micro-clearfix-hack/ */
.clearfix:before, .clearfix:after { content: " "; display: table; }
.clearfix:after { clear: both; }

body {
font-family: "Helvetica Neue",Helvetica,Arial,'Microsoft YaHei',sans-serif,'Lato', Calibri;
color: #777;
background: #f6f6f6;
}

a {
color: #555;
text-decoration: none;
outline: none;
}

a:hover,
a:active {
color: #777;
}

a img {
border: none;
}
/* Header Style */
.main,
.container > header {
margin: 0 auto;
/*padding: 2em;*/
}

.container {
height: 100%;
}

.container > header {
padding-top: 20px;
padding-bottom: 20px;
text-align: center;
background: rgba(0,0,0,0.01);
}

.container > header h1 {
font-size: 2.625em;
line-height: 1.3;
margin: 0;
font-weight: 300;
}

.container > header span {
display: block;
font-size: 60%;
opacity: 0.3;
padding: 0 0 0.6em 0.1em;
}

/* Main Content */
.main {
/*max-width: 69em;*/
width: 100%;
height: 100%;
overflow: hidden;
}
.demo-scroll{
overflow-y: scroll;
width: 100%;
height: 100%;
}
.column {
float: left;
width: 50%;
padding: 0 2em;
min-height: 300px;
position: relative;
}

.column:nth-child(2) {
box-shadow: -1px 0 0 rgba(0,0,0,0.1);
}

.column p {
font-weight: 300;
font-size: 2em;
padding: 0;
margin: 0;
text-align: right;
line-height: 1.5;
}

/* To Navigation Style */
.htmleaf-top {
background: #fff;
background: rgba(255, 255, 255, 0.6);
text-transform: uppercase;
width: 100%;
font-size: 0.69em;
line-height: 2.2;
}

.htmleaf-top a {
padding: 0 1em;
letter-spacing: 0.1em;
color: #888;
display: inline-block;
}

.htmleaf-top a:hover {
background: rgba(255,255,255,0.95);
color: #333;
}

.htmleaf-top span.right {
float: right;
}

.htmleaf-top span.right a {
float: left;
display: block;
}

.htmleaf-icon:before {
font-family: 'codropsicons';
margin: 0 4px;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
}



/* Demo Buttons Style */
.htmleaf-demos {
padding-top: 1em;
font-size: 0.9em;
}

.htmleaf-demos a {
display: inline-block;
margin: 0.2em;
padding: 0.45em 1em;
background: #999;
color: #fff;
font-weight: 700;
border-radius: 2px;
}

.htmleaf-demos a:hover,
.htmleaf-demos a.current-demo,
.htmleaf-demos a.current-demo:hover {
opacity: 0.6;
}

.htmleaf-nav {
text-align: center;
}

.htmleaf-nav a {
display: inline-block;
margin: 20px auto;
padding: 0.3em;
}
.bb-custom-wrapper {
width: 420px;
position: relative;
margin: 0 auto 40px;
text-align: center;
}
/* Demo Styles */

.demo-1 body {
color: #87968e;
background: #fff2e3;
}

.demo-1 a {
color: #72b890;
}

.demo-1 .htmleaf-demos a {
background: #72b890;
color: #fff;
}

.demo-2 body {
color: #fff;
background: #c05d8e;
}

.demo-2 a {
color: #d38daf;
}

.demo-2 a:hover,
.demo-2 a:active {
color: #fff;
}

.demo-2 .htmleaf-demos a {
background: #883b61;
color: #fff;
}

.demo-2 .htmleaf-top a:hover {
background: rgba(255,255,255,0.3);
color: #333;
}

.demo-3 body {
color: #87968e;
background: #fff2e3;
}

.demo-3 a {
color: #ea5381;
}

.demo-3 .htmleaf-demos a {
background: #ea5381;
color: #fff;
}

.demo-4 body {
color: #999;
background: #fff2e3;
overflow: hidden;
}

.demo-4 a {
color: #1baede;
}

.demo-4 a:hover,
.demo-4 a:active {
opacity: 0.6;
}

.demo-4 .htmleaf-demos a {
background: #1baede;
color: #fff;
}

.demo-5 body {
background: #fffbd6;
}
/****/
.related {
/*margin-top: 5em;*/
color: #fff;
background: #333;
text-align: center;
font-size: 1.25em;
padding: 3em 0;
overflow: hidden;
}

.related a {
display: inline-block;
text-align: left;
margin: 20px auto;
padding: 10px 20px;
opacity: 0.8;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
-webkit-backface-visibility: hidden;
}

.related a:hover,
.related a:active {
opacity: 1;
}

.related a img {
max-width: 100%;
}

.related a h3 {
font-weight: 300;
margin-top: 0.15em;
color: #fff;
}

@media screen and (max-width: 40em) {

.htmleaf-icon span {
display: none;
}

.htmleaf-icon:before {
font-size: 160%;
line-height: 2;
}

}

@media screen and (max-width: 46.0625em) {
.column {
width: 100%;
min-width: auto;
min-height: auto;
padding: 1em;
}

.column p {
text-align: left;
font-size: 1.5em;
}

.column:nth-child(2) {
box-shadow: 0 -1px 0 rgba(0,0,0,0.1);
}
}

@media screen and (max-width: 25em) {

.htmleaf-icon span {
display: none;
}

}

核心js

/**
* 检测2个物体是否碰撞
* @param object1 物体1
* @param object2 物体2
* @param overlap 允许重叠的大小
* @returns {Boolean} 如果碰撞了,返回true
*/
function CheckIntersect(object1, object2, overlap)
{
// x-轴 x-轴
// A1------>B1 C1 A2------>B2 C2
// +--------+ ^ +--------+ ^
// | object1| | y-轴 | object2| | y-轴
// | | | | | |
// +--------+ D1 +--------+ D2
//
//overlap是重叠的区域值
A1 = object1.x + overlap;
B1 = object1.x + object1.size - overlap;
C1 = object1.y + overlap;
D1 = object1.y + object1.size - overlap;

A2 = object2.x + overlap;
B2 = object2.x + object2.size - overlap;
C2 = object2.y + overlap;
D2 = object2.y + object2.size - overlap;

//假如他们在x-轴重叠
if(A1 >= A2 && A1 <= B2
|| B1 >= A2 && B1 <= B2)
{
//判断y-轴重叠
if(C1 >= C2 && C1 <= D2 || D1 >= C2 && D1 <= D2)
{
return true;
}
}
return false;
}

/**
* 坦克与地图块碰撞
* @param tank 坦克对象
* @param mapobj 地图对象
* @returns {Boolean} 如果碰撞,返回true
*/
function tankMapCollision(tank,mapobj){
//移动检测,记录最后一次的移动方向,根据方向判断+-overlap,
var tileNum = 0;//需要检测的tile数
var rowIndex = 0;//map中的行索引
var colIndex = 0;//map中的列索引
var overlap = 3;//允许重叠的大小

//根据tank的x、y计算出map中的row和col
if(tank.dir == UP){
rowIndex = parseInt((tank.tempY + overlap - mapobj.offsetY)/mapobj.tileSize);
colIndex = parseInt((tank.tempX + overlap- mapobj.offsetX)/mapobj.tileSize);
}else if(tank.dir == DOWN){
//向下,即dir==1的时候,行索引的计算需要+tank.Height
rowIndex = parseInt((tank.tempY - overlap - mapobj.offsetY + tank.size)/mapobj.tileSize);
colIndex = parseInt((tank.tempX + overlap- mapobj.offsetX)/mapobj.tileSize);
}else if(tank.dir == LEFT){
rowIndex = parseInt((tank.tempY + overlap- mapobj.offsetY)/mapobj.tileSize);
colIndex = parseInt((tank.tempX + overlap - mapobj.offsetX)/mapobj.tileSize);
}else if(tank.dir == RIGHT){
rowIndex = parseInt((tank.tempY + overlap- mapobj.offsetY)/mapobj.tileSize);
//向右,即dir==3的时候,列索引的计算需要+tank.Height
colIndex = parseInt((tank.tempX - overlap - mapobj.offsetX + tank.size)/mapobj.tileSize);
}
if(rowIndex >= mapobj.HTileCount || rowIndex < 0 || colIndex >= mapobj.wTileCount || colIndex < 0){
return true;
}
if(tank.dir == UP || tank.dir == DOWN){
var tempWidth = parseInt(tank.tempX - map.offsetX - (colIndex)*mapobj.tileSize + tank.size - overlap);//去除重叠部分
if(tempWidth % mapobj.tileSize == 0 ){
tileNum = parseInt(tempWidth/mapobj.tileSize);
}else{
tileNum = parseInt(tempWidth/mapobj.tileSize) + 1;
}
for(var i=0;i<tileNum && colIndex+i < mapobj.wTileCount ;i++){
var mapContent = mapobj.mapLevel[rowIndex][colIndex+i];
if(mapContent == WALL || mapContent == GRID || mapContent == WATER || mapContent == HOME || mapContent == ANOTHREHOME){
if(tank.dir == UP){
tank.y = mapobj.offsetY + rowIndex * mapobj.tileSize + mapobj.tileSize - overlap;
}else if(tank.dir == DOWN){
tank.y = mapobj.offsetY + rowIndex * mapobj.tileSize - tank.size + overlap;
}
return true;
}
}
}else{
var tempHeight = parseInt(tank.tempY - map.offsetY - (rowIndex)*mapobj.tileSize + tank.size - overlap);//去除重叠部分
if(tempHeight % mapobj.tileSize == 0 ){
tileNum = parseInt(tempHeight/mapobj.tileSize);
}else{
tileNum = parseInt(tempHeight/mapobj.tileSize) + 1;
}
for(var i=0;i<tileNum && rowIndex+i < mapobj.HTileCount;i++){
var mapContent = mapobj.mapLevel[rowIndex+i][colIndex];
if(mapContent == WALL || mapContent == GRID || mapContent == WATER || mapContent == HOME || mapContent == ANOTHREHOME){
if(tank.dir == LEFT){
tank.x = mapobj.offsetX + colIndex * mapobj.tileSize + mapobj.tileSize - overlap;
}else if(tank.dir == RIGHT){
tank.x = mapobj.offsetX + colIndex * mapobj.tileSize - tank.size + overlap;
}
return true;
}
}
}
return false;
}

/**
* 子弹与地图块的碰撞
* @param bullet 子弹对象
* @param mapobj 地图对象
*/
function bulletMapCollision(bullet,mapobj){
var tileNum = 0;//需要检测的tile数
var rowIndex = 0;//map中的行索引
var colIndex = 0;//map中的列索引
var mapChangeIndex = [];//map中需要更新的索引数组
var result = false;//是否碰撞
//根据bullet的x、y计算出map中的row和col
if(bullet.dir == UP){
rowIndex = parseInt((bullet.y - mapobj.offsetY)/mapobj.tileSize);
colIndex = parseInt((bullet.x - mapobj.offsetX)/mapobj.tileSize);
}else if(bullet.dir == DOWN){
//向下,即dir==1的时候,行索引的计算需要+bullet.Height
rowIndex = parseInt((bullet.y - mapobj.offsetY + bullet.size)/mapobj.tileSize);
colIndex = parseInt((bullet.x - mapobj.offsetX)/mapobj.tileSize);
}else if(bullet.dir == LEFT){
rowIndex = parseInt((bullet.y - mapobj.offsetY)/mapobj.tileSize);
colIndex = parseInt((bullet.x - mapobj.offsetX)/mapobj.tileSize);
}else if(bullet.dir == RIGHT){
rowIndex = parseInt((bullet.y - mapobj.offsetY)/mapobj.tileSize);
//向右,即dir==3的时候,列索引的计算需要+bullet.Height
colIndex = parseInt((bullet.x - mapobj.offsetX + bullet.size)/mapobj.tileSize);
}
if(rowIndex >= mapobj.HTileCount || rowIndex < 0 || colIndex >= mapobj.wTileCount || colIndex < 0){
return true;
}

if(bullet.dir == UP || bullet.dir == DOWN){
var tempWidth = parseInt(bullet.x - map.offsetX - (colIndex)*mapobj.tileSize + bullet.size);
if(tempWidth % mapobj.tileSize == 0 ){
tileNum = parseInt(tempWidth/mapobj.tileSize);
}else{
tileNum = parseInt(tempWidth/mapobj.tileSize) + 1;
}
for(var i=0;i<tileNum && colIndex+i < mapobj.wTileCount ;i++){
var mapContent = mapobj.mapLevel[rowIndex][colIndex+i];
if(mapContent == WALL || mapContent == GRID || mapContent == HOME || mapContent == ANOTHREHOME){
//bullet.distroy();
result = true;
if(mapContent == WALL){
//墙被打掉
mapChangeIndex.push([rowIndex,colIndex+i]);
}else if(mapContent == GRID){

}else{
isGameOver = true;
break;
}
}
}
}else{
var tempHeight = parseInt(bullet.y - map.offsetY - (rowIndex)*mapobj.tileSize + bullet.size);
if(tempHeight % mapobj.tileSize == 0 ){
tileNum = parseInt(tempHeight/mapobj.tileSize);
}else{
tileNum = parseInt(tempHeight/mapobj.tileSize) + 1;
}
for(var i=0;i<tileNum && rowIndex+i < mapobj.HTileCount;i++){
var mapContent = mapobj.mapLevel[rowIndex+i][colIndex];
if(mapContent == WALL || mapContent == GRID || mapContent == HOME || mapContent == ANOTHREHOME){
//bullet.distroy();
result = true;
if(mapContent == WALL){
//墙被打掉
mapChangeIndex.push([rowIndex+i,colIndex]);
}else if(mapContent == GRID){

}else{
isGameOver = true;
break;
}
}
}
}
//更新地图
map.updateMap(mapChangeIndex,0);
return result;
}

原文地址:https://blog.csdn.net/weixin_41937552/article/details/116559485


0 个评论

要回复文章请先登录注册