码迷,mamicode.com
首页 > 其他好文 > 详细

DS课设【坦克大战最短路】(MummyDing)

时间:2015-02-27 11:55:05      阅读:168      评论:0      收藏:0      [点我收藏+]

标签:


DS课设【坦克大战最短路】

     还是决定写点东西简单记录下这次编码。

一、想法

    还没放假的时候只想着用C#实现,算法图论方面觉得图论方向会靠谱些,但一直没有什么好点子。C#以前也没学过,自信来源于MFC的学习经历(以前也是用它做了C语言课设)。C#应该是没有MFC那么复杂的,心想看几天应该就可以上手一些小东西了,事实证明也如此。
    寒假时间相对以前更长,也并不着急做课设。开始一段是刷题+学习Kinect+顺带了解Kinect,后来在刷题过程中遇到这题,还蛮有意思的,当即就写了个“坦克大战最短路简单设计”:

坦克大战最短路
算法:优先队列+广度优先搜索
用户自定义地图
1.	设置地图大小起点和终点 设置弹药
2.	设置铁墙、土墙、河水
3.	
模式一:用户自己走
模式二:自动给出最短路
对比
坦克方法:
1.	调整方向
2.	开枪
事件:
判断是否达到
是否可以走
是否打到墙(子弹能可以穿过河)
游戏是否结束
人性化:
背景音乐
子弹效果
保存地图
当前最短
爆炸效果

二、实现

当时也没有十足把握能实现它,搁置了几天。期间看了一些关于C#项目开发的教学视频,也是跟着例程做了下,心里有底了,首先实现UI是没有问题的。
然后就有了下面这个详细设计文档:
一、界面
1、地图
大小 12*12  每个方格 边长60个像素
左上角为原点(0,0)
2、对象
坦克(Tank)
砖块(Brick)  1次打破
钢墙(Steel)  2次打破
河水(River)  子弹可以穿过,坦克不可以
星星(Star)
子弹(Bullet)
3、菜单
添加对象
手动模式
自动模式
4、显示
当前消耗
帮助
二、类设计
1.游戏父类 GameObject
属性:
x,y,width,height,image
方法:
构造函数(初始化)
Draw();
GetRectangle();
2.墙壁父类 WallFather:GameObject
属性:
+life
方法:
构造函数(初始化)
重写Draw();
IsOver();
3.River&Star父类 RSFather:GameObject
方法:
构造函数(初始化)
重写Draw();
4.Bullet :GameObject
属性:
+power
方法:
构造函数(初始化)
重写Draw();
5.Tank:GameObject
属性:

方法:
重写Draw(g);
Move();
Fire();
6.SingleObject
方法:
GetSingle();
Draw();
Check();
三、事件响应


 于是照着这个,一个一个类的实现了。在这个过程中,颇有面向对象的意思在里面,以前写MFC程序的时候感触没这么明显。


1.新建一个VC++ win32 DLL
2.添加头文件"" 在头文件中加入以下代码:
#ifndef DLL_EXPORT
#define DECLDIR __declspec(dllimport)
#else
#define DECLDIR __declspec(dllexport)
#endif
3.添加源程序,添加以下代码:
#define DLL_EXPORT //先定义宏
#include "Dll.h"//这个头文件必须在#define DLL_EXPORT后面
4.编写自己需要的函数:
extern "C"
{
	DECLDIR 返回值 函数名(参数...)
	{
	
	}
}
5.添加必要的头文件,不需要main函数,编译即可
6.建立一个C#工程,将生成的Dll文件(在Debug目录下)扔到C#工程Bin
目录下。
7.在C#中添加如下代码:
(1). 命名空间
using System.Runtime.InteropServices; 
(2).声明
 [DllImport("CppDll.dll", SetLastError = true)]
 private static extern 返回值 函数名(参数...);
 8.然后这个函数就可以直接在C#工程中使用了







这期间,特别要注意的是C++和C#之间数据类型的对应关系!! 这里着实费了一番功夫。
附上C++与C#数据类型对照表:
C++            C#
=====================================
WORD            ushort
DWORD            uint
UCHAR            int/byte   大部分情况都可以使用int代替,而如果需要严格对齐的话则应该用bytebyte 
UCHAR*            string/IntPtr
unsigned char*         [MarshalAs(UnmanagedType.LPArray)]byte[]/?(Intptr)
char*            string
LPCTSTR            string
LPTSTR            [MarshalAs(UnmanagedType.LPTStr)] string
long            int
ulong               uint
Handle            IntPtr
HWND            IntPtr
void*            IntPtr
int            int
int*            ref int
*int            IntPtr
unsigned int        uint
COLORREF                uint

这个很实用。
然后我把以前的代码做了简单修改就可以了,不过只能是把最短的路径长度算出来,还没有记录路径的功能。
我C++的代码中是直接调用STL提供的“优先队列”实现的BFS,这样记录路径稍微麻烦点~ 后来是想着在结点专门开个变量记录路径。最后代码被我改成了这个样子[变量名什么的不大规范]:
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#define N 15
#define Max_Len 100
#define DLL_EXPORT //先定义宏
#include "Dll.h"//这个头文件必须在#define DLL_EXPORT后面
using namespace std;
int n, m, sx, sy, ex, ey, visit[N][N];
int dir[][2] = {
	{ 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 }
};
char chess[N][N];
enum TankAcationType
{
	//Move
	MoveStep,
	//Fire Move
	FireOneWall,
	//Fire Fire Move
	FireTwoWall,

	//Dir->Up Move
	ChangeTheDirToUp,
	//Dir->Up Fire
	ChangeToUpFireOne,
	//Dir->Up Fire Fire
	ChangeToUpFireTwo,

	//Dir->Down Fire
	ChangeToDownFireOne,
	//Dir->Down Move
	ChangeTheDirToDown,
	//Dir->Down Fire Fire
	ChangeToDownFireTwo,

	//Dir->Left Move
	ChangeTheDirToLeft,
	//Dir->Left Fire
	ChangeToLeftFireOne,
	//Dir->Left Fire Fire
	ChangeToLeftFireTwo,

	//Dir->Right Move
	ChangeTheDirToRight,
	//Dir->Right Fire
	ChangeToRightFireOne,
	//Dir->Right Fire Fire
	ChangeToRightFireTwo,
	None
	
} TmpType;
TankAcationType TmpPath[Max_Len];
struct Node{
	int x, y, s,d;
	int step;
	TankAcationType Path[Max_Len];
	friend bool  operator <(Node a, Node b){
		return a.s>b.s;
	}
};
bool ok(int x, int y){
	if (x >= 0 && x<m&&y >= 0 && y<n&&chess[x][y] != 'R'&&chess[x][y] != '#')
		return true;
	return false;
}

extern "C"
{
	DECLDIR int bfs( char  * Map,TankAcationType  * ActionPath,int &Count){
		m = 15;
		n = 15;
		for (int i = 0; i < 15; i++)
		for (int j = 0; j < 15; j++)
			chess[i][j] = '#';
		for (int i = 0; i < 12;i++)
		for (int j = 0; j < 11; j++)
			chess[i][j] = '.';
		for (int i = 0; i<n; i++)
		for (int j = 0; j < m; j++){
			if (Map[i*m + j] == '#'&&chess[i][j] == '.')continue;
			chess[i][j] = Map[i*m + j];
		}
		for (int i = 0; i<m; i++)
		for (int j = 0; j<n; j++){
			if (chess[i][j] == 'T'){
				sx = i; sy = j;
			   chess[i][j] = 'R';
			}
			else if (chess[i][j] == 'X'){
				ex = i; ey = j;
				chess[i][j] = '.';
			}
		}
	
		priority_queue<Node> q;
		memset(visit, -1, sizeof(visit));
		visit[sx][sy] = 0;
		Node head = { sx, sy, 0 ,1,0};
		head.d = 1;
		q.push(head);
		while (!q.empty())
		{
			Node f = q.top();
			q.pop();
			if (f.x == ex&&f.y == ey){ 
				for (int i = 0; i < f.step; i++)
					ActionPath[i] = f.Path[i];
				Count =  f.step;
				return f.s; 
			}
			for (int i = 0; i<4; i++){
				int dx = f.x + dir[i][0], dy = f.y + dir[i][1];
				if (ok(dx, dy) && visit[dx][dy]){
					visit[dx][dy] = 0;
					int temp = 0;
					if (chess[dx][dy] == 'S'){
						temp = 3; 
						if (f.d==i)
						TmpType = FireTwoWall;
						else {
							temp++;
							switch (i)
							{
							case 0:
								TmpType = ChangeToDownFireTwo;
								break;
							case 1:
								TmpType = ChangeToUpFireTwo;
								break;
							case 2:
								TmpType = ChangeToRightFireTwo;
								break;
							case 3:
								TmpType = ChangeToLeftFireTwo;
								break;
							}
						}
					}
					else if (chess[dx][dy] == 'B'){ 
						temp = 2; 
						if(f.d==i)TmpType = FireOneWall;
						else {
							temp++;
							switch (i)
							{
							case 0:
								TmpType = ChangeToDownFireOne;
								break;
							case 1:
								TmpType = ChangeToUpFireOne;
								break;
							case 2:
								TmpType = ChangeToRightFireOne;
								break;
							case 3:
								TmpType = ChangeToLeftFireOne;
								break;
							}
						}
					}
					else if (chess[dx][dy] == '.') {
						temp = 1; 
						if(f.d==i)TmpType = MoveStep;
						else {
							temp++;
							switch (i)
							{
							case 0:
								TmpType = ChangeTheDirToDown;
								break;
							case 1:
								TmpType = ChangeTheDirToUp;
								break;
							case 2:
								TmpType = ChangeTheDirToRight;
								break;
							case 3:
								TmpType = ChangeTheDirToLeft;
								break;
							}
						}
					}
					for (int k = 0; k < f.step; k++)
						TmpPath[k] = f.Path[k];
					TmpPath[f.step] = TmpType;
					Node tmp = { dx, dy, f.s + temp};
					for (int k = 0; k <= f.step; k++)
					 tmp.Path[k] = TmpPath[k];
					tmp.step = f.step + 1; 
					tmp.d = i;
					q.push(tmp);
				}
			}
		}
		return -1;
	}
}

70行变200行,吓尿【一些代码有点傻,懒得改了】
C#显示路径那段代码是这个样子的:
 #region AutoRun 方法
        public static void AutoRun()
        {
            for (int i = 0; i <TotalStep; i++)
            {
                switch (GameObject.AcationPath[i])
                {
                    case TankAcationType.ChangeTheDirToUp:
                        MessageBox.Show("向上");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Up;
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeTheDirToDown:
                        MessageBox.Show("向下");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Down;
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeTheDirToLeft:
                        MessageBox.Show("向左");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Left;
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeTheDirToRight:
                        MessageBox.Show("向右");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Right;
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.FireOneWall:
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.FireTwoWall:
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                         SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.MoveStep:
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToDownFireOne:
                        MessageBox.Show("向下");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Down;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToDownFireTwo:
                        MessageBox.Show("向下");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Down;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToLeftFireOne:
                        MessageBox.Show("向左");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Left;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToLeftFireTwo:
                        MessageBox.Show("向左");
                        GameObject.Cost++;
                         SingleObject.GetObject().T.Dir = Direction.Left;
                         MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToRightFireOne:
                        MessageBox.Show("向右");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Right;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToRightFireTwo:
                        MessageBox.Show("向右");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Right;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToUpFireOne:
                        MessageBox.Show("向上");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Up;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                    case TankAcationType.ChangeToUpFireTwo:
                        MessageBox.Show("向上");
                        GameObject.Cost++;
                        SingleObject.GetObject().T.Dir = Direction.Up;
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("开火");
                        SingleObject.GetObject().T.Fire();
                        MessageBox.Show("向前");
                        SingleObject.GetObject().T.Move();
                        break;
                }
            }
        }
        #endregion

这个过程中出现了好几个隐蔽的bug,大大小小——有些东西根本无法依靠debug找出来。。。。


这些东西完成之后就剩下一些细节了【“新游戏”启动界面 什么的】 之前想过的“导入地图”等功能现在还不想动了【应该可以利用文件操作实现吧】,开学再慢慢研究*_*

三、类图[在新标签页打开查看大图]

技术分享


四、效果


技术分享


技术分享


技术分享


【转载请注明出处】
 作者:MummyDing


                                                                                                                                                                                                                           2015年2月27日




DS课设【坦克大战最短路】(MummyDing)

标签:

原文地址:http://blog.csdn.net/mummyding/article/details/43965923

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!