废话(前言)

现在游戏多了,不过总是感觉不太对自己的口味,每个游戏都感觉和自己想象中的要差了那么一点点,所以我决定尝试着自己写一个游戏。

因为从来没做过游戏开发,所以所有游戏机制的实现都只能是在网上查或者自己摸索,如果大家在看文章( 或视频 )的时候有更好的实现方法,请一定要告诉我。

那么策划下这个游戏吧,我的口味是怎样的呢:

  1. 随时可以玩。由于我上班的时候有时候觉得很无聊,也许想放松一下,所以最好这个游戏是网页版的,随时打开随时玩。
  2. 最好是要用到脑子玩。哼,大家辣么聪明,肯定要靠智力虐人的对吧,那么应该是一个能让人越动脑就越厉害的游戏。
  3. 不要氪金。消费内容一定不能影响游戏平衡,或者干脆没有消费内容,装备全靠打。
  4. 有没有伙伴,有多少伙伴,都能玩。希望喜欢单机挑战的小伙伴能玩,情侣能玩,宿舍能玩,很多朋友一起也能玩。

综上所述,我决定做的这个游戏就是!!!——炉石传说的网页版,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是什么版本的

回复 weizaomao 取消回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用*标注