一套跨平台五子棋网游的开发经历(一)

闲来无事,因自己想要在服务器开发方面进行更深入的学习,积累更丰富的经验。决定写一套网络游戏的c/s。

因为主要目的是服务器的开发,因此游戏我选用规则较为简单、画面特效没有要求的回合制游戏:五子棋。我曾经在刚接触编程的时候自己在控制台下做过这个游戏,当时写的ai特nb我自己根本下不赢他。确定是制作五子棋了, 但是还要满足跨平台的特性,毕竟移动互联时代,得终端者得天下。游戏做成全平台才能更好的将各种玩家聚集在一起。跨平台?b/s是人们通常会第一个想到的跨平台方式,的确现在市面上有很多基于b/s的页游,大部分使用的是flash作为游戏引擎。但手机上很少有人使用浏览器玩游戏。(其实根本不会flash,html也烂得很,曾经给别人用php做的数据管理网站根本就没有像样的界面)于是选择了c++的跨平台游戏引擎cocos2dx,这引擎简单好用,而且因为是c++作为游戏逻辑,移植特方便,以前也用过这个引擎(某比赛)。最终选用的版本是cocos2d-x 3.4。

既然是网络游戏的服务器,那么就得高效,而且是在linux下,因此我选epoll模型进行服务端的开发,epoll的部分写在这篇文章里:epoll模型的理解与封装实现,使用的linux系统为CENT OS 6.4,内核为linux2.6。

关于游戏开发步骤的思考:

因缺乏游戏这方面的经验,按照自己以前习惯的套路来说,通信方式与协议的设计应该是放在首位的,然后是服务器、再到客户端(没有美工)。

而自己以前曾经玩到很多的单机游戏,更新版本后,游戏便增加了网络游戏功能。这似乎说明了很多游戏与网络协议之间是相互独立的。甚至网络协议是根据实际的游戏逻辑设计的,而不是游戏根据协议来设计自身的逻辑。

最终决定先把单机的版本做出来。于是制定了如下的开发流程:

1、游戏的算法与数据结构设计与实现

2、游戏交互设计与实现

3、单机游戏的实现

4、游戏通信协议设计

5、服务器实现(不可忽略掉的重点,自己写游戏的目的)

6、网络游戏功能实现

7、平台移植

1、游戏的算法与数据结构设计与实现:

    五子棋这个游戏是一个二维平面上的游戏,我们将棋盘看做一个数组,每一个格子的状态分为两种:没棋和有棋,有棋因不同玩家而区别(数量不限,可直接作为多人多子棋的游戏基类)

代码:

//Chess.h
#ifndef  _CHESS_H_
#define  _CHESS_H_
#include "cocos2d.h"
USING_NS_CC;

//下棋坐标状态结构体
struct Chesspos
{
	int x,y;
	int player;//该步数所属玩家
	Chesspos(){};
	Chesspos(int px,int py,int pp)
	{
		x=px;
		y=py;
		player=pp;
	}
};

class Chessway
{
	Chesspos *way;//路径数组
	int totallen;//总长度
	int len;//当前步数

public:
	Chessway(int totalnum);
	~Chessway(void);
	void setempty();
	bool addway(int x,int y,int player);//添加步数
	int getstep();
	Chesspos getnow();
};

class Chess
{
public:
	Chess(int width,int heigh,int winlen=5,int playernum=2);
	~Chess(void);

	int **board;
	int w,h;
	int pnum; //palyer num
	int wlen; //how number can win
	Chessway *way;
	int playercnt;//player start at 1
	bool isgameend;

	bool init(int width,int heigh,int winlen=5,int playernum=2);
	void exit();

	void restart();
	bool nextstep(Chesspos np);//下棋,自动判断玩家
	bool nextstep(int x,int y);

	int getstatus(int x,int y);//获取游戏状态

	bool checklen(int x,int y);
	int checkwin();//判断游戏是否结束并返回胜利玩家
};

#endif //_CHESS_H_

检测胜利的逻辑很简单:找到一个下有棋的位置,检查这个位置下、右、左下、右下是否有连续相等的5个棋,即为游戏胜利。游戏一旦胜利是不可以继续下棋的,所以只会有一个玩家胜利。下面给出判断代码:

//Chess.cpp
//胜利检测代码
bool Chess::checklen(int x,int y)
{
	for(int i=1;i<wlen;i++)
	{
		if(x+i>=w)
		{
			break;
		}
		if(board[x+i][y]!=board[x][y])
		{
			break;
		}
		if(i==wlen-1)
		{
			return true;
		}
	}

	for(int i=1;i<wlen;i++)
	{
		if(y+i>=h)
		{
			break;
		}
		if(board[x][y+i]!=board[x][y])
		{
			break;
		}
		if(i==wlen-1)
		{
			return true;
		}
	}

	for(int i=1;i<wlen;i++)
	{
		if(x+i>=w||y+i>=h)
		{
			break;
		}
		if(board[x+i][y+i]!=board[x][y])
		{
			break;
		}
		if(i==wlen-1)
		{
			return true;
		}
	}

	for(int i=1;i<wlen;i++)
	{
		if(x-i<0||y+i>=h)
		{
			break;
		}
		if(board[x-i][y+i]!=board[x][y])
		{
			break;
		}
		if(i==wlen-1)
		{
			return true;
		}
	}
	return false;
}
int Chess::checkwin()
{
	for(int i=0;i<w;i++)
	{
		for(int j=0;j<h;j++)
		{
			if(board[i][j])
			{
				if(checklen(i,j))
				{
					isgameend=true;
					return board[i][j];
				}
			}
		}
	}
	return 0;
}

 2、游戏交互设计与实现

涉及到游戏交互,这里就要使用到游戏引擎了。首先需要把游戏的一些图片资源大致搞定,这里用画图这画了几个不堪入目的图片资源:    别看这画的丑,我可是用鼠标和window自带的画图画出来的,到时候在游戏中看起来是毫无违和感的(笔者小学就会画H漫了)。

这里就要用到cocos2dx的东西了。首先为每一个下棋的格子设计一个个块状的节点,然后设计游戏主体布景层:

class ChessNode:public Node
class ChessMain:public Layer

作为游戏棋盘,每一个格子的形态都是一样的,我只需要将它们拼接成矩阵就成了一个完整的棋盘。因此在游戏布景层里,我开了一个Vector的ChessNode,将其依次紧凑地排列在屏幕上。在游戏初始状态时,chess_1.png、chess_2.png是不会显示的,如图(截图我直接使用现成游戏的截图):

这样的棋盘看起来是不是很没有违和感?

当下棋后,就可以把对应的棋图显示出来:

后面发现好像真正的下棋是下在十字交叉处的。。

这部分的注意事项主要就在于触摸检测与棋盘屏幕大小。触摸的话计算相对棋盘布景层的坐标可以得出下棋的位置。棋盘就以静态值480px为标准,在其他地方调用的时候缩放即可。

#ifndef  _CHESSMAIN_H_
#define  _CHESSMAIN_H_
#include "cocos2d.h"
#include "Chess.h"
USING_NS_CC;

#define defaultwinsize 480.0
#define chesspicsize 50.0

static Point winsize;

class ChessNode:public Node
{
	public:
	ChessNode(int playernum=2);

	Vector<Sprite *> chesspicarr;
	Sprite * basepic;
};

class ChessMain:public Layer
{
public:

	Chess *chessdata;

	Vector<ChessNode *> basenode;

	virtual bool init();
	//virtual void onEnter();

	void restart();
	void updateone(int x,int y);
	void updateall();

	bool nextstep(int x,int y);
	int  checkwin();

	CREATE_FUNC(ChessMain);
};

#endif //_CHESSMAIN_H_
#include "ChessMain.h"

ChessNode::ChessNode(int playernum)
{
	basepic=Sprite::create("chess_base_1.png");
	basepic->setAnchorPoint(ccp(0,0));
	this->addChild(basepic);

	char addname[]="chess_1.png";
	for(int i=0;i<playernum;i++)
	{
		addname[6]=‘0‘+i+1;
		auto newsprite=Sprite::create(addname);
		chesspicarr.pushBack(newsprite);
		chesspicarr.back()->setAnchorPoint(ccp(0,0));
		this->addChild(chesspicarr.back());

	}
}

bool ChessMain::init()
{
	winsize=Director::sharedDirector()->getWinSize();

	//默认值棋盘
	chessdata=new Chess(15,15);

	for(int i=0;i<chessdata->w;i++)
	{
		for(int j=0;j<chessdata->h;j++)
		{
			basenode.pushBack(new ChessNode());

			basenode.back()->setScale((defaultwinsize/chessdata->h)/chesspicsize);
			basenode.back()->setPosition(
				ccp(defaultwinsize/chessdata->w*i,defaultwinsize/chessdata->h*j)
				);
			basenode.back()->setAnchorPoint(ccp(0,0));

			this->addChild(basenode.back());
		}
	}

	restart();

	return true;
}
/*
void ChessMain::onEnter()
{
	;
}
*/
void ChessMain::restart()
{
	chessdata->restart();
	updateall();
}
void ChessMain::updateone(int x,int y)
{
	for(int i=0;i<chessdata->pnum;i++)
	{
		if(chessdata->getstatus(x,y)==i+1)
		{
			basenode.at(x*chessdata->w+y)->
				chesspicarr.at(i)->setVisible(true);
		}
		else
		{
			basenode.at(x*chessdata->w+y)->
				chesspicarr.at(i)->setVisible(false);
		}
	}
}
void ChessMain::updateall()
{
	for(int i=0;i<chessdata->w;i++)
	{
		for(int j=0;j<chessdata->h;j++)
		{
			updateone(i,j);
		}
	}
}

bool ChessMain::nextstep(int x,int y)
{
	if(chessdata->isgameend)
	{
		return false;
	}
	if(!chessdata->nextstep(x,y))
	{
		return false;
	}
	updateone(x,y);
	checkwin();

	return true;
}

int ChessMain::checkwin()
{
	return chessdata->checkwin();
}

/*
bool ChessMain::onTouchBegan(Touch *touch, Event *unused_event)
{
	Point pos=convertTouchToNodeSpace(touch);

	if(pos.x>defaultwinsize||pos.y>defaultwinsize)
	{
		return false;
	}
	int x=chessdata->w*(pos.x/defaultwinsize);
	int y=chessdata->h*(pos.y/defaultwinsize);

	return nextstep(x,y);
}
*/

这里的触摸函数会由以后ChessMain的子类重写。

3、单机游戏的实现

单机游戏,只需写好对手的AI逻辑即可。幸好是五子棋不是围棋,AI很好写,能很快计算出必胜态。由于自己主要目的是写网络端。因此我把单机功能实现后并没有写AI,把接口留着的,只接了一个随机函数,等以后有闲情把AI逻辑加上。

总的来说这部分就是加上了进入游戏前的菜单以及单机游戏的选项和游戏结束的对话框:

#ifndef  _AIGAMEMAIN_H_
#define  _AIGAMEMAIN_H_
#include "cocos2d.h"
#include "ChessMain.h"
USING_NS_CC;

#define defaulttoolwidth 200.0
#define defaulttoolheight 100.0

//游戏结束菜单
class AIGameEndTool:public Layer
{
public:
	AIGameEndTool(int type);
	bool init(int type);

	void gameRestart(Ref* pSender);
	void menuCloseCallback(Ref* pSender);
};

//AI游戏继承于ChessMain
class AIGameMain:public ChessMain
{
public:
	virtual bool init();

	virtual bool onTouchBegan(Touch *touch, Event *unused_event);
	void nextaistep();
	bool checkwin();
	CREATE_FUNC(AIGameMain);
};

#endif //_AIGAMEMAIN_H_

现在一个能玩的游戏已经完成,接下来是重点的网络部分。

等后文档的写好了在这里给出网络部分文章的地址以及源码文件,敬请期待。

时间: 2024-10-18 22:47:04

一套跨平台五子棋网游的开发经历(一)的相关文章

一套跨平台五子棋网游的开发经历

闲来无事,因自己想要在服务器开发方面进行更深入的学习,积累更丰富的经验.决定写一套网络游戏的c/s. 因为主要目的是服务器的开发,因此游戏我选用规则较为简单.画面特效没有要求的回合制游戏:五子棋.我曾经在刚接触编程的时候自己在控制台下做过这个游戏,当时写的ai特nb我自己根本下不赢他.确定是制作五子棋了, 但是还要满足跨平台的特性,毕竟移动互联时代,得终端者得天下.游戏做成全平台才能更好的将各种玩家聚集在一起.跨平台?b/s是人们通常会第一个想到的跨平台方式,的确现在市面上有很多基于b/s的页游

一套跨平台五子棋网游的开发经历(二)

接上文http://wchrt.blog.51cto.com/8472636/1661524 4.游戏通信协议设计 因为是PC.手机都能玩的游戏,考虑到糟糕的手机网络环境,通信采用客户端单方发起请求,服务器回复的方式,使服务器不用考虑确保手机信号不好或IP变更的情况,类似于web方式. 游戏没有设计固定的用户,采用的是游戏每次向服务器申请一个游戏ID,使用这个游戏ID在互联网上和其他用户对战.于是协议报文设计了两种:普通请求/回复报文gamequest.游戏数据报文nextquest. #inc

Unity网游开发生存指南—蒸汽之城

Posted by amy on 2013.03.07 文 / 王楠(梦加网络 游戏制作人) 前段时间关于Unity是否适合国内手游/网游创业团队的讨论非常火爆,本文从<蒸汽之城>的开发历程谈起,对于国内网游团队是否应该选择Unity引擎,以及如何解决使用Unity开发网游时遇到的各种主要问题进行讨论. 厦门梦加的蒸汽之城  <蒸汽之城>是厦门梦加网络的第一款作品,使用Unity引擎制作的蒸汽朋克风3D实时战斗MMORPG页游.游戏拥有幻想工业时代恢弘苍凉的场景:丰富的种族.职业和

转游戏开发做的第一款手机网游的经历和体会

转游戏开发大半年以来,做过的游戏不多,刚开始就写单机版的游戏,不过也不多.后来就接触手机版网游,第一款游戏就是超级英雄,目前这款游戏还在升级维护当中,首次发布就是五月初,第一个月的收入就过千万.关于这款游戏直接看截图效果吧! 以上就是有关该款游戏的截图,有喜欢这款游戏可以下载试玩下,有想学的也可以下载看看交流下经验. 我以前是学C#,主要做桌面类型的软件会的语言也不多,也用过C++.在去年年底就接触到了cocos2d-x,了解到它是跨平台的然后就决心转手游开发了,初期阶段就看书,自己做些东西,也

决战JavaScript服务端网游开发

在经过三年多的手游客户端学习与开发练习后,我发现仅满足于前端仅能在游戏开发中占居一席之地.要想全面深入理解手游开发逻辑,特别是如今的网络时代的手游数据管理及通信逻辑,必须掌握一定的服务器端开发技术.作一个十分相似的比较,就像Web开发,只懂得前端技术(HTML/HTML5/CSS/DOM/BOM/JQUERY/JS),也只能是具备了一条腿走路的能力,而两条腿走路则更是每一个程序员所期望的.因此,作为一名优秀的Web前端程序员最好对于 服务器端开发技术也有一定程度的了解. 值得欣喜的是,相当多的程

张左峰的歪理邪说 之 对于瀑布式开发和敏捷开发在网游开发中的应用

本周小孩送回姥爷姥姥家,终于有时间更新一下自己的微博了,三年没更新了,我真TMD懒惰!我错了....这次努力更新一些东西 有些人问我,为啥不去一些大点的微博站写这些内容.我觉得没有必要啊,反正早晚都会被搜索引擎爬到,哪里都一样. 本文纯理论,是一个思想指导,你完全照搬,你就输了....尽可能写的雅俗共赏一些,一起研究学习进步! 正文开始.....(哪那么多废话...果然人老了) 首先,我们要明确两个概念 瀑布式开发:瀑布式,顾名思义,自上而下,连绵不绝,稳步推进.瀑布式开发,是一个我们最常规的开

VC++实战《星际传奇》网游课程第一部分网络游戏开发基础篇(游戏引擎设计)

本系列课程基于最新的DirectX11接口进行深入细致的讲解,内容涉及D3D11原理与应用.DirectInput.DirectSound等: 教程中专门针对新兴的D3D11接口展开深入的讲解,详细讲解了D3D11渲染管线.DirectComputer(参看<VC++游戏开发系列之Directcomputer并行计算原理与实践--DX11游戏实战开发>).Tessellation.多线程渲染.Shader动态链接等新内容.新知识.并且基于这些内容的基础,更进一步讲解了光照模型原理及实现.高级的

C++网络游戏零基础开发视频教程(300+课时,MINI快跑、水果忍者、DirectX技术、天鹰教3D网游)

C++网络游戏零基础开发视频教程(300+课时,MINI快跑.水果忍者.DirectX技术.天鹰教3D网游)下载联系QQ:1026270010 距离二十世纪八十年代c++语言的诞生,到如今已经有三十多年的历史,随着c++语言的发展,它被应用于在越来越多的领域.C++是一个多泛型的编程语言,它既可以面向过程,也是一门面向对象的语言.C++是一门使用非常广泛的计算机编程语言,因此它受到了越来越多程序员的亲睐.C++相关专家介绍:c++的就业领域很广泛,相对于java和其他语言来说有很多优点,很多大公

[盘点]现今热门的h5网游

各位好久不见,过年的休息时间已经结束,大家休息了一周,又要开始新的一年的奋斗了!于是小编从回来的第一天就开始花时间自己整理了一篇文章,所有各位是不是应该夸一夸小编呢?因为小编又要开始分享“干货”了.(都是小编自己的一些理解,若有错误或不足,请提出) 各位在过年的这一段时间里,都干了些啥呢? 别的不说,肯定有很多单身的童鞋们被长辈们同时逼问事业与爱情上的种种事情,当然小编也是一样,然后各种躲着不肯见人,于是无聊地掏出自己的手机,打开游戏,思考人生. (强行回归话题,感觉没啥关系,其实是扯淡) 说到