废话(前言)
现在游戏多了,不过总是感觉不太对自己的口味,每个游戏都感觉和自己想象中的要差了那么一点点,所以我决定尝试着自己写一个游戏。
因为从来没做过游戏开发,所以所有游戏机制的实现都只能是在网上查或者自己摸索,如果大家在看文章( 或视频 )的时候有更好的实现方法,请一定要告诉我。
那么策划下这个游戏吧,我的口味是怎样的呢:
- 随时可以玩。由于我上班的时候有时候觉得很无聊,也许想放松一下,所以最好这个游戏是网页版的,随时打开随时玩。
- 最好是要用到脑子玩。哼,大家辣么聪明,肯定要靠智力虐人的对吧,那么应该是一个能让人越动脑就越厉害的游戏。
- 不要氪金。消费内容一定不能影响游戏平衡,或者干脆没有消费内容,装备全靠打。
- 有没有伙伴,有多少伙伴,都能玩。希望喜欢单机挑战的小伙伴能玩,情侣能玩,宿舍能玩,很多朋友一起也能玩。
综上所述,我决定做的这个游戏就是!!!——炉石传说的网页版,emmmmmm……,没事,先模仿再提高吧。
然后再说一下这个视频的受众和需要的基础,因为这个视频没有使用高深的技术,所以想学习到很底层技术的同学可能会失望,这是游戏线上beta版本的地址,大家可以去试玩一下,如果觉得感兴趣,自己也想开发一款这样的游戏,那么你可以继续看了。
我会将代码按批次开源,这个项目在视频出完之前,暂时不接受贡献,因为会打乱我视频的节奏,希望大家理解,等项目出完之后,会接受pull request,所以现阶段对项目想改善的可以直接向我提意见。
由于是网页版本的游戏,那么需要一定的前端基础,当然没有基础的你也可以图个乐子。既然是个网络游戏,那么肯定是有后端的,所以需要掌握一点后端思想。最次,你也要知道编程是个什么东西,然后才能享受看别人敲代码的乐趣。
下面就是正文内容,我们就先进行技术选型,把框架搭起来,数据交互能够联通。
第一回合(准备开始)
先安装需要的库,不使用多厉害的游戏引擎了,就使用最方便开发的技术来实现。那我就选择了vue,如果有同学想用react或者angular或者jquery,都可以的。那我们先安装vue,vue-router,先把游戏的页面搭建出来。
这里我就使用vue-cli,这个呢是vue官方出的一个脚手架,就是帮你初始化一个项目的。那我们先在命令行里执行:
npm install -g @vue/cli
先在全局安装好vue-cli,然后执行vue-cli的命令:
vue create card-game-client
选择Manually select features,会有一些选项要求选择,我们只需要:vue-router,vuex,babel
vue-router为了以后发布方便不使用history模式。
然后再要初始化一个后台项目,创建文件夹card-game-server,为了方便,我就直接用nodejs作为游戏后台了,先初始化一个express项目,在目录下执行:
npm init
初始化npm,然后再安装需要的库:
npm i express body-parser cors log4js --save
不过作为一个游戏来说,肯定数据交互是不能用http请求的,那就使用websocket技术来和后台进行数据交互,我就使用socketio这个库了,继续在命令行执行:
npm i socket.io --save
好。那么就开始激动人心的第一步,hello world。
首先大家在client项目的src文件夹下把原来的views删除,创建一个叫pages的文件夹,存放我们的页面,这一步很重要,它主要目的是——我比较喜欢pages这个单词。创建以后游戏主要场景页面,GameTable.vue:
<template>
<div></div>
</template>
<script>
export default {
name: "GameTable"
}
</script>
在GameTable加载的完成后,安装socketio客户端。
npm i socket.io-client --save
自动连接服务器,并发送一个hello的消息,并监听即将收到的服务器的消息,显示在页面:
<template>
<div>{{message}}</div>
</template>
<script>
import * as io from 'socket.io-client';
export default {
name: "GameTable",
data() {
return {
message: ""
}
},
mounted() {
this.socket = io.connect('http://localhost:4001');
this.socket.emit('hello');
this.socket.on("world", args => {
this.message = args.message;
});
}
}
</script>
页面写好之后,不要忘记修改router,打开router.js,删除之前默认配置,把我们的新页面加进去:
import Vue from 'vue'
import Router from 'vue-router'
import GameTable from './pages/GameTable.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'gameTable',
component: GameTable
}
]
})
App.vue中也有一些默认生成的dom和css,删除掉不需要的:
<template>
<div id="app">
<router-view/>
</div>
</template>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
客户端发送写好了,来写一下服务端处理程序,首先在server的文件夹下创建app.js文件,在app.js文件中初始化服务器:
let http = require('http');
let express = require('express');
let cors = require('cors');
let bodyParser = require('body-parser');
let socket = require("socket.io");
let app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
let server = http.createServer(app);
let socketServer = socket(server);
server.listen(4001, function () {
console.log("listen");
});
socketServer.on('connection', function (socket) {
console.log("connect one on :" + new Date().toLocaleString());
socket.on('hello', function () { // 监听hello事件
socket.emit('world', {
message: 'hello world'
})
});
socket.on('disconnect', function () {
console.log("disconnect one on :" + new Date().toLocaleString());
});
});
let socketServer = socket(server);这行代码创建了一个socket服务,然后通过监听事件和发送消息来和客户端互动。
接下来就在server目录运行node app.js,在client目录运行npm run serve,就能运行前端和后端,如果在页面上看到hello world,就证明框架搭建完成了。
接下来,继续下一步,对战界面的绘制。
根据市面上的卡牌游戏设计,对战最基本是有两个角色栏(对战双方),两个手牌区(对战双方),一张对战桌。所以,我们的对战区域就长这样:
先把桌面和己方手牌区域画出来,在GameTable.vue中,添加下面的代码:
<div class="app">
<div class="table">
<div class="other-card-area">
</div>
<div class="my-card-area">
</div>
</div>
<div class="my-card">
</div>
</div>
为了让界面更加容易区分,我们给各个区域加上鲜明的颜色:
.app {
width: 100%;
height: 100%;
overflow: hidden;
user-select: none;
}
.my-card {
position: absolute;
bottom: 20px;
width: 100%;
min-height: 170px;
display: flex;
justify-content: center;
background: #f00;
}
.table {
width: 100%;
height: 100%;
}
.my-card-area {
width: 100%;
height: 33%;
min-height: 170px;
position: absolute;
bottom: 210px;
display: flex;
padding: 10px;
box-sizing: border-box;
justify-content: center;
background-color: #0f0;
}
.other-card-area {
width: 100%;
min-height: 170px;
padding: 10px;
display: flex;
justify-content: center;
box-sizing: border-box;
flex-wrap: wrap;
background-color: #00f;
}
那么看到的效果应该是这样的:
做到这一步,相信大家也能看出这个游戏的雏形了吧……emmmmmm,下一章进度加快吧。
再发一次游戏最新进度的线上beta版地址,大家看了有兴趣的,就等更新吧~
14 条评论
vbyzc · 2019年1月7日 09:37
想从头教到尾吗,那得几十章?
xiejingyang · 2019年1月7日 10:46
哈哈哈,怕是要哦,我自己研究就研究了大半年,尽量精简点语言
lalala · 2019年2月1日 13:32
照这种情况,十几章很正常。不过可以把重点部分弄出来,然后比较简单的知识用其他已有的链接替代方式处理,这样就能减少点文章数目,更新起来也会比较快
xiejingyang · 2019年2月8日 20:08
恩恩,好的~
谢谢建议~
更新慢只能是我太懒了哈哈哈,我以后面尽量精简到重点,或者直接用文字和视频一起发布,这样应该会更容易看懂~
qinnn · 2019年5月30日 13:14
博主,我准备仿这你这个做个类似的卡牌游戏了
xiejingyang · 2019年6月3日 14:36
好啊。 一起讨论啊
baing · 2019年9月17日 03:05
真的太关键了。非常感谢博主,我自己也打算写一个线上的卡牌游戏。。(我感觉我离写出weboxss.com又进了一步。
xiejingyang · 2019年9月17日 11:18
加油啊,我也会加紧迭代的~话说,weboxss是啥?
baing · 2019年9月17日 23:17
其实是我打错了,应该是webxoss,那是卡牌游戏wixoss的web版,现在维护的人去工作了了,就没在做了。。他的源码放在了github上,搜索webxoss就行,但是是一个老项目(不确保还有多少价值。。)我现在就想用现在主流的技术去重新实现webxoss。。看了一天的博主的代码,真的对我这种刚刚毕业的菜鸡太关键了。。另外,博主也可以关注下营火战争:http://static.iyingdi.com/firefightol/index.html?nologin=1,或许对您有点帮助。。
weizaomao · 2020年12月1日 15:22
照着博主的写的,但是在socketio连接的时候报错了,好像是跨域问题
xiejingyang · 2020年12月2日 14:49
本地连本地怎么会跨域呢?
weizaomao · 2020年12月3日 09:46
虽然都是本地但是前端是8080端口,服务端是4001端口,这不是跨域了吗?不知道博主是怎么解决的
weizaomao · 2020年12月3日 12:34
已解决,博主已经在服务端代码里用了cors处理跨域,我还是出现跨域是因为依赖包的版本不一样,版本改成和博主的一样就能正常连接了。话说我一开始用webpackdevserver设置了代理之后也能连接,但是会不停地发请求,最后会报错Connection closed before receiving a handshake response,查了半天也不知道是什么问题。
yad · 2021年7月21日 23:22
请问一下您用的npm是什么版本的