显示标签为“技术文章”的博文。显示所有博文
显示标签为“技术文章”的博文。显示所有博文

2009年5月9日星期六

一种自动的singleton工具

singleton单件模式,但建立一个自动的singleton工具就是一个学问了。
看下面描述。

基类singleton
template<class T>
class singleton
{
static T* ms_Singleton;
public:
singleton(void)
{
assert(!ms_Singleton);
int offset = (int)(T*)1-(int)(singleton<T>*)(T*)1;
ms_Singleton = (T*)((int)this+offset);
}
~singleton()
{
assert(ms_Singleton);
ms_Singleton = 0;
}

static T& GetSingleton(void)
{
assert(ms_Singleton);
return (*ms_Singleton);
}

static T* GetSingletonPtr(void)
{
return ms_Singleton;
}
};

template<class T>
T* singleton<T>::ms_Singleton = 0;

派生类

class A : public singleton<A>
{
public:
void outputA() {cout << "10";}
};

使用

static A g_A;
int main()
{
A::GetSingleton().outputA();
return 0;
}

整个来说就是一个比较好的模式。

2009年5月8日星期五

xerces-c环境配置以及测试

xerces-c环境配置。
编译xerces projects目录下的lib工程会生成2个文件dll和lib。
然后添加src目录。
include D:\vs2005\xerces-c-3.0.1\src
library D:\vs2005\xerces-c-3.0.1\Build\Win32\VC9\Release
就是编译生成lib和dll的那个目录

然后在projects-property下链接xerces-c_3.lib
或者#pragma comment(lib,"xerces-c_3.lib")也行

好配置成功了。

下面该做练习了。
我随意在google找了个代码,自己敲上去,有bug,修改了下,
成功解析。
代码还是蛮简单的。


游戏编程精粹里面有讲到数据驱动的,以后数据就存xml。。。

上代码和发图才是王道

sample.xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<ApplicationSettings option_a="10" option_b="24">
</ApplicationSettings>

<OtherStuff option_x="500">
</OtherStuff>
</root>




parser.h
#ifndef XML_PARSER_H
#define XML_PARSER_H


#include <xercesc\dom\DOM.hpp>
#include <xercesc\dom\DOMDocument.hpp>
#include <xercesc\dom\DOMDocumentType.hpp>
#include <xercesc\dom\DOMElement.hpp>
#include <xercesc\dom\DOMImplementation.hpp>
#include <xercesc\dom\DOMImplementationLS.hpp>
#include <xercesc\dom\DOMNodeIterator.hpp>
#include <xercesc\dom\DOMNodeList.hpp>
#include <xercesc\dom\DOMText.hpp>
#include <xercesc\parsers\XercesDOMParser.hpp>
#include <xercesc\util\XMLUni.hpp>

#include <string>
#include <stdexcept>
#include <iostream>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

enum
{
ERROR_ARGS = 1,
ERROR_XERCES_INIT,
ERROR_PARSE,
ERROR_EMPTY_DOCUMENT
};

class GetConfig
{
public:
GetConfig();
~GetConfig();
void readConfigFile(std::string&) throw(std::runtime_error);

char* getOptionA() {return m_optionA;}
char* getOptionB() {return m_optionB;}
private:
xercesc::XercesDOMParser* m_ConfigFileParser;
char* m_optionA;
char* m_optionB;

XMLCh* TAG_root;
XMLCh* TAG_ApplicationSettings;
XMLCh* ATTR_OptionA;
XMLCh* ATTR_OptionB;
};

parser.cpp

#include "Parser.h"

using namespace std;
using namespace xercesc;

GetConfig::GetConfig()
{
try
{
XMLPlatformUtils::Initialize();
}
catch(XMLException& e)
{
char* message = XMLString::transcode(e.getMessage());
cerr << "XML toolKit initializtion error:"
<< message << endl;
XMLString::release(&message);
}

TAG_root = XMLString::transcode("root");
TAG_ApplicationSettings = XMLString::transcode("ApplicationSettings");


ATTR_OptionA = XMLString::transcode("option_a");
ATTR_OptionB = XMLString::transcode("option_b");

m_ConfigFileParser = new XercesDOMParser;
}

GetConfig::~GetConfig()
{
try
{
XMLPlatformUtils::Terminate();
}
catch(xercesc::XMLException& e)
{
char* message = XMLString::transcode(e.getMessage());
cerr << "XML toolkit teerdown error" << message << endl;
XMLString::release(&message);
}
}

void GetConfig::readConfigFile(std::string& configfile)
throw(std::runtime_error)
{
struct stat fileStatus;

int iretStat = stat(configfile.c_str(),&fileStatus);
if (iretStat == ENOENT)
throw(std::runtime_error("Path name does not exsit"));else if(iretStat == ENOTDIR)
throw(std::runtime_error("a component of the path is not a directory"));
//else if(iretStat == ELOOP)
// throw(std::runtime_error("Too many symbolic links encountered while
traversing the path"));
else if(iretStat == EACCES)
throw(std::runtime_error("Permission denied"));
else if(iretStat == ENAMETOOLONG)
throw(std::runtime_error("File can not be read\n"));

m_ConfigFileParser->setValidationScheme(XercesDOMParser::Val_Never);
m_ConfigFileParser->setDoNamespaces(false);
m_ConfigFileParser->setDoSchema(false);
m_ConfigFileParser->setLoadExternalDTD(false);

try
{
m_ConfigFileParser->parse(configfile.c_str());

DOMDocument* xmlDoc = m_ConfigFileParser->getDocument();

DOMElement* elementroot = xmlDoc->getDocumentElement();

if(! elementroot) throw(std::runtime_error("empty XML document"));

DOMNodeList* children = elementroot->getChildNodes();
const XMLSize_t nodeCount = children->getLength();

for(XMLSize_t xx = 0; xx < nodeCount; xx++)
{
DOMNode* currentNode = children->item(xx);

if (currentNode->getNodeType() &&
currentNode->getNodeType() == DOMNode::ELEMENT_NODE)
{
DOMElement* currentElement = dynamic_cast<xercesc::DOMElement*>(currentNode);
if (XMLString::equals(currentElement->getTagName(),TAG_ApplicationSettings))
{
const XMLCh* xmlch_OptionA = currentElement->getAttribute(ATTR_OptionA);
m_optionA = XMLString::transcode(xmlch_OptionA);
const XMLCh* xmlch_OptionB = currentElement->getAttribute(ATTR_OptionB);
m_optionB = XMLString::transcode(xmlch_OptionB);
break;
}
}
}
}

catch(xercesc::XMLException& e)
{
char* message = xercesc::XMLString::transcode(e.getMessage());
ostringstream errBuf;
errBuf << "Error parseing file" << message << flush;
XMLString::release(&message);
}
}

test.cpp
#include <iostream>
#include "Parser.h"
#include <string>
using namespace std;


int main()
{
string configfilename = "sample.xml";
GetConfig appConfig;
appConfig.readConfigFile(configfilename);

cout << "Application Option A=" << appConfig.getOptionA() << endl;
cout << "Application Option B=" << appConfig.getOptionB() << endl;
return 0;

}

2009年5月7日星期四

数据结构作业 练习函数指针

3种排序算法就不说了,插入排序,快速排序,选择排序。
这算法我是记住了又忘记了,忘记后又要记。。。太无语了
最中要的是函数指针的用法。直接看代码.
我太郁闷了,这博客居然要把直接复制代码html会解析出错。
逼我把代码先弄成html然后再复制上来。。。

#include <vector>
#include <iostream>
#include <algorithm>
#include <time.h>
using namespace std;

typedef vector<int> ArrayInt;

typedef void (*sort_type)(ArrayInt&);
void insersion_sort(ArrayInt& arr);
void selection_sort(ArrayInt& arr);
void quick_sort(ArrayInt& arr);

int max_key(ArrayInt &arr,int low,int hight);
void recursive_quick_sort(ArrayInt& arr,int low,int hight);
int partition(ArrayInt& arr,int low,int hight);

void my_test_sort(ArrayInt& arr,sort_type p)
{
(*p)(arr);
}

void insersion_sort(ArrayInt& arr)
{
int first_unsorted;
int position;
int current;

for (first_unsorted = 1;first_unsorted < arr.size();first_unsorted++)
{
if (arr[first_unsorted] < arr[first_unsorted-1])
{
position = first_unsorted;
current = arr[first_unsorted];
do
{
arr[position] = arr[position-1];
position--;
}while(position>0 && arr[position-1] >current);

arr[position] = current;
}

}

}

void selection_sort(ArrayInt& arr)
{
for(int position = arr.size()-1;position>0;position--)
{
int max = max_key(arr,0,position);
swap(arr[max],arr[position]);
}

}

void quick_sort(ArrayInt& arr)
{
recursive_quick_sort(arr,0,arr.size()-1);
}


void recursive_quick_sort(ArrayInt& arr,int low,int hight)
{
int pivot_position;
if (low < hight)
{
pivot_position = partition(arr,low,hight);
recursive_quick_sort(arr,low,pivot_position-1);
recursive_quick_sort(arr,pivot_position+1,hight);
}
}

int partition(ArrayInt& arr,int low,int hight)
{
int iRndPivID;
srand(unsigned(time(0)));
iRndPivID = (rand() % (hight - low + 1)) + low;
swap(arr[iRndPivID], arr[hight]);
int iPivValue;
int i;
int iPivPos;
iPivValue = arr[hight];
iPivPos = low - 1;

for (i=low; i<=hight-1; i++)
{
if (arr[i] <= iPivValue)
{
iPivPos++;
swap(arr[iPivPos],arr[i]);
}
}
iPivPos++;
swap(arr[iPivPos],arr[hight]);
return iPivPos;

}
int max_key(ArrayInt &arr,int low,int hight)
{
int largest,current;
largest = low;
for(current = low+1;current<=hight;current++)
{
if (arr[largest] < arr[current])
{
largest = current;
}
}
return largest;
}


int main()
{
ArrayInt arrayInt;
int st;
cout <<"请输入排序算法\n1.插入排序\n2.选择排序\n3.快速排序\n";
cin >> st;
sort_type p = NULL;
switch(st)
{
case 1:
p = insersion_sort;
break;
case 2:
p = selection_sort;
break;

case 3:
p = quick_sort;
break;

default:
p = quick_sort;

}
cout << "请输入整数的个数后,然后输入整数"<<endl;
int n;
cin>>n;
for (int i=0;i<n;i++)
{
int num;
cin>>num;
arrayInt.push_back(num);
}
cout << "排序前的数字为:"<<endl;
for (int i=0;i<n;i++)
{
cout << arrayInt[i] << endl;
}

my_test_sort(arrayInt,p);
cout << "排序后的数字为:"<<endl;
for (int i=0;i<n;i++)
{
cout << arrayInt[i] << endl;
}

return 0;
}



2009年5月6日星期三

容器erase的时候不会释放的指针指向的内存

一切看代码
主要是想用map管理无模式对话框,但不知道map erase的时候会不会释放掉内存。用map测试下。

//这个破博客 居然打不出来尖括号
#include "map"
#include "iostream"
#include "string"
using namespace std;


class A
{
public:
A(int _x,int _y){_x = x; _y =y;}

public:
int x;
int y;
};
typedef map stringAMap;





int main()
{
stringAMap map1;
A* a = new A(100,200);
map1.insert(make_pair("韦宇浩",a));
map1.insert(make_pair("陆一峰",new A(200,300)));
stringAMap::iterator pos;
pos = map1.find("韦宇浩");

if (pos != map1.end())
{
map1.erase(pos);
}
cout <<>x;
return 0;

}


看结果 便知,没有释放掉内存。








2009年5月5日星期二

初识OpenGL


主要是那本 计算机图形学是用OpenGL写的。

自己下glut麻烦,浩哥果然是万能的,找他要了个库......


将 .h 复制到C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include\gll

.lib复制到C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib,

将所有的dll扔进C:\Windows\system32


好搭建成功。启动vs2008,敲上试验代码。

好,截图。

2009年5月4日星期一

singleton设计模式

先看一个最简单的Singleton

class Singleton1
{
public:
Singleton1& Instance()
{
static Singleton Obj;
return Obj;

private:
Singleton1();
}
}
优雅的解决了问题。

再看一个可扩展的。


#include
#include
class SingletonBase
{
public:
SingletonBase() { cout << "SingletonBase created!" << endl; }

virtual ~SingletonBase() { cout << "SingletonBase destroyed!" << endl; }

virtual void Access() { cout << "SingletonBase accessed!" << endl; }

static SingletonBase* GetObj() { return m_pObj; }

static void SetObj(SingletonBase* pObj) { m_pObj = pObj; }

protected:
static SingletonBase* m_pObj;

};

SingletonBase* SingletonBase::m_pObj;

inline SingletonBase* Base()
{
assert(SingletonBase::GetObj());
return SingletonBase::GetObj();
}
// Create a derived singleton-type class
class SingletonDerived : public SingletonBase
{
public:
SingletonDerived()
{ cout << "SingletonDerived created!" << endl; }

virtual ~SingletonDerived()
{ cout << "SingletonDerived destroyed" << endl; }

virtual void Access()
{ cout << "SingletonDerived accessed!" << endl; }

protected:

};

inline SingletonDerived* Derived()
{
assert(SingletonDerived::GetObj());
return (SingletonDerived*)SingletonDerived::GetObj();
}
// Using the code...
// The complex singleton requires more work to use, but is
// more flexible. It also allows more control over object
// creation, which is sometimes desirable.

SingletonDerived::SetObj(new SingletonDerived);

// Notice that the functionality has been overridden by the new
// class, even though accessing it through the original method.

Base()->Access();
Derived()->Access();

// This variation on a singleton unfortunately requires both
// explicit creation and deletion.

delete SingletonDerived::GetObj();

一个可扩展的singleton。
主要用于需要大量访问的全局对象。

2009年5月3日星期日

吐血中 CEGUI

搞了半天 CEGUI初始化。

刚开始一个库找不到,在目录里翻了半天,搞定。

后来运行link,找不到d3dx9_40.dll,在system32目录下差了下,dx版本才到37,更新了下dx。

现在好了,出了个未知错误,new CEGUI::System(myRenderer);一旦new,马上弹出windows 警告框,头大。

搞了半天初始化不了,郁闷啊.......

这个GUI还真是复杂

DirectX 创建设备


好久都没碰DX了,今天将创建设备的代码重新敲了一遍,还是那么麻烦。。。


主要是想学习啊CEGUI。


游戏引擎阿,游戏引擎。


还是截图吧。

初识QT

QT,强大的c++UI设计库,我很后悔为什么不早点知道QT,也不用痛苦的学习MFC了。



跨平台的库。简单的几句代码就可以创建窗口,window有点类似c#的form。



刚开始学。Qslider,QSpinBox.



发图了。

2009年4月20日星期一

辩论赛计时器发布







前几天 抽空做了个辩论赛计时器,校辩论队用得。

周日亲眼看到自己写的代码运行在多人面前,感觉很不一般。
代码量:3000

其中代码冗余的地方很多。而且运行的时候还出了点小bug.

不碍事,今天完美无bug版本终于做好了。

还是发几张图吧,下周是决赛,就等看了。






2009年4月10日星期五

CWinApp::OnFileNew CWinApp::OnFIleOpen CDocument::OnOpenDocument() CDocument::ONNewDocument

单说CArchive,在单文档应用程序中确实方便,但是要是MFC做得是辅助程序,比如地图编辑器,那么生成的文档必然不能在游戏中用,若要用的话得引入MFC库,凭空使程序增加了大小。

单文档ID_File_New 根据我的理解 会调用CWinApp::OnfileNew ,然后CwinApp::OnfileNew 调用 CDocument::OnNewDocument 这个就不用改了。
注意:经过我的实验OnNewDocument是不能用 afx_msg映射的

CwinApp::OnFIleOpen 也是这样都会调用CDocument::OnOpenDocument(name) 注意,CDocument::OnOpenDocument 会调用serilize()函数,这样会串行化,所以在覆盖的时候不要调用CDocument::OnOpenDocument, 那个传入的name 就是弹出CFIleDilog中文件的名字,我估计那个弹出对话框的工作是CWinApp::OnOpenFile()做得。

至于CDocument::OnFileSave() 这个的对话框显示是CDocument做得,也会调用serilize()函数,所以覆盖的时候不要调用基类的OnFileSave就行了,至于对话框的显示,自己做一个吧,就用CFileDialog,很简单。


后面说说OnUpdateAllViews()

Invalidate()是让程序重画窗口。
UpdateAllViews()是在DOC/VIEW结构中,当一个视图的数据改变后,通知所有视图作相应的改变,和重画毫无关系。

Invalidate()是使窗口无效,使系统向其发WM_PAINT消息,使的程序的OnPaint被调用重画客户区。

而UpdateAllViews()是文档与视之间的联系,调用从它会使程序与此文档相关的所有视的UpdateView被调用至于是否重画以及怎么画是由各视的UpdateView来决定的。

Invalidate()是Cwnd的成员函数,与DOC-VIEW无关;
UpdateAllViews是CDocument的成员函数,具体体现DOC-VIEW的精神。

如果仅重画当前窗口用 this->Invalidate(); 如果通知所有和当前文档相关的窗口重画用GetDocument()->UpdateAllViews()(在View中)或this->UpdateAllViews()(在Doc中)。

Invalidate()函数产生一条WM_PAINT消息,并送入windows消息队列中,是窗口产生重画。 而,UpdateAllViews 并不进入windows消息队列中,直接产生重画
*******************************************
Invalidate函数的总结
InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效

InvalidateRect函数中的参数TRUE表示系统会在你画之前用背景色将所选区域覆盖一次,默认背景色为白色,可以通过设置BRUSH来改变背景色。

Invalidate()之后:(MFC的,顺便了)OnPaint()->OnPrepareDC()->OnDraw()所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。
Invalidate标记一个需要重绘的无效区域,并不意味着调用该函数后就立刻进行重绘。类似于PostMessage(WM_PAINT),需要处理到WM_PAINT消息时才真正重绘。以为您Invalidate之后还有其他的语句正在执行,程序没有机会去处理WM_PAINT消息,但当函数执行完毕后,消息处理才得以进行。

Invalidate只是放一个WM_PAINT消息在队列里,不做别的,所以只有当当前函数返回后,进入消息循环,取出WM_PAINT,才执行PAINT,所以不管Invalidate放哪里,都是最后的。

InvalidateRect(hWnd,&rect,TRUE);向hWnd窗体发出WM_PAINT的消息,强制客户区域重绘制,rect是你指定要刷新的区域,此区域外的客户区域不被重绘,这样防止客户区域的一个局部的改动,而导致整个客户区域重绘而导致闪烁,如果最后的参数为TRUE,则还向窗体发送WM_ERASEBKGND消息,使背景重绘,当然在客户区域重绘之前。UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT

如果希望立即刷新无效区域,可以在调用InvalidateRect之后调用UpdateWindow,如果客户区的任一部分无效,则UpdateWindow将导致Windows用WM_PAINT消息调用窗口过程(如果整个客户区有效,则不调用窗口过程)。这一WM_PAINT消息不进入消息队列,直接由WINDOWS调用窗口过程。窗口过程完成刷新以后立刻退出,WINDOWS将控制返回给程序中UpdateWindow调用之后的语句。(windows程序设计第5版 P98)
UpdateData()顺便说下,这个函数不是刷新界面用的。UpdateData();参数为FALSE时,将界面上控件绑定的变量的数据导到控件内,参数为TRUE时,导入方向则相反。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1618571
onDraw和onPaint函数的区别
CWnd::OnPaint() 是CWnd类的成员函数,是WM_PAINT 消息的响应函数.当你调用CWnd::UpdateWindow(),CWnd::RedrawWindow()时, 或者窗口被其他窗口覆盖,改变大小等事件就会产生WM_PAINT 消息. 你可以在CWnd派生的子类中使用它.

CView::OnDraw()是CView类的一个纯虚函数.你要使用,必须重载它.

CView::OnPaint()会调用它.OnPaint是收到WM_PAINT消息时所调用的,
OnPaint调用OnDraw函数,而由于OnDraw是虚拟函数,你可以自己改写,所以就实现了,不同的绘图目的ondraw用于cview,而onpaint用于cdialogonpait 响应WM_PAINT消息,在CView中,它接着调用OnDraw CPaintDC响应wm_paint消息,自动完成绘制 ClientDC实时绘制一般情况下,你看不到两个函数有什么太大区别,因为一般情况下,它们的任务都是重画窗口。

在CView及其子类中,恰如浪刀所言,OnDraw是由OnPaint调用的,也就是你可以像在窗口类中一样,调用Invalidate函数导致,OnDraw的调用(当然,Invalidate是先导致OnPaint的调用)。 要说OnDraw和OnPaint有什么区别,那就是它除了可以把画画到屏幕外,还可以把它画到其它任何标准输出设备,如打印机


最后再说下OnUpdate()

CView::OnUpdatevirtual void OnUpdate( CView* pSender, LPARAM lHint, CObject* pHint );
参数:
pSender 指向修改了文档的视图,如果需要更新所有的视图,则为NULL。 lHint 包含了与修改有关的信息。 pHint 指向保存了与修改有关的信息的对象。

说明:框架在视图的文档被修改后调用这个函数;这个函数CDocument::UpdateAllViews调用的,使视图能够更新它的显示以反映那些变化。它也被OnInitialUpdate的缺省实现所调用。缺省的实现使整个客户区域无效,使得下一次接收到WM_PAINT消息时重画这些区域。如果你只想更新与文档的修改部分对应的区域,则应重载这个函数。为此你必须利用提示参数传递有关修改的信息。如果要使用lHint,则应定义特殊的提示值,通常是位掩码或枚举值,并且使文档传递其中的一个值。要使用pHint,则应从CObject集成一个提示类,并使文档传递提示对象的指针。当你重载OnUpdate函数的时候,应使用CObject::IsKindOf成员函数来确定提示对象的运行时类型。通常你不用在OnUpdate中直接执行任何绘图操作。相反,确定以设备坐标表示的矩形,描述要更新的区域,将这个矩形传递给CWnd::InvalidateRect。这会使下一次接收到WM_PAINT消息时产生绘图操作。如果lHint为0,pHint为NULL,文档将发送一个一般的更新通知。如果一个视图接收到了一般的更新通知,或者它没有解码出提示,它将会使整个客户区无效。

2009年4月8日星期三

思考

关于文件储存的问题,昨天晚上和今天早上看了一下MFC中CFile,CStdioFile,CArchive,个人感觉MFC中文档的串行化是一个很方便的东西。

地图编辑器文件储存格式的话,针对于魔塔那个小游戏,我想一般的txt就应该够了,但是这并不是一个好办法,我想到了暑假中学习过得xml,是的,xml确实是储存信息的好方法,可以自己设计树形结构,精确的查找定位。

关于c++中读写xml,我去搜了一下,阿帕奇的xerces-c重量级的xml parser,花些时间研究研究吧。

2009年4月7日星期二

BrowseInfo ShellExcute

看来还是少玩游戏的好,从12点到3点完了鬼泣,然后睡了一觉,就到5点了,哎,失误啊。不多说了。
学到的是选择文件夹对话框的创建。
关键字BrowseInfo。ItemIDlist

CString sFolder;
LPMALLOC pMalloc;
// Gets the Shell's default allocator
if (::SHGetMalloc(&pMalloc) == NOERROR)
{
BROWSEINFO bi;
char pszBuffer[MAX_PATH];
LPITEMIDLIST pidl;

bi.hwndOwner = GetDesktopWindow();
bi.pidlRoot = NULL;
bi.pszDisplayName = pszBuffer;
bi.lpszTitle = _T("Select a directory...");
bi.ulFlags = BIF_RETURNFSANCESTORS BIF_RETURNONLYFSDIRS;
bi.lpfn = NULL;
bi.lParam = 0;
// This next call issues the dialog box.
if ((pidl = ::SHBrowseForFolder(&bi)) != NULL)
{
if (::SHGetPathFromIDList(pidl, pszBuffer))
{
// At this point pszBuffer contains the selected path
sFolder = pszBuffer;
}
// Free the PIDL allocated by SHBrowseForFolder.
pMalloc->Free(pidl);
}
// Release the shell's allocator.
pMalloc->Release();
}


还有就是ShellExcute()这个函数可以根据路径运行一个相应程序或者打开一个文件。补充下:关于第二个参数执行的操作类型,个人在main()函数尝试了下,有以下理解。
"open"表示打开
"explore"可以打开文件夹,不过是用资源管理器打开
"find"会直接调用windows自带的那个搜索器

以前看到浩哥做得那个在shell下输入ps就可以运行Photoshop,输入qq就可以运行qq,想起来也就是用在本地文件保存了一张表,这张表对应了相应的快捷键和路径,然后用shellExecute运行....
ShellExecute的功能是运行一个外部程序(或者是打开一个已注册的文件、打开一个目录、打印一个文件等等),并对外部程序有一定的控制。
  有几个API函数都可以实现这些功能,但是在大多数情况下ShellExecute是更多的被使用的,同时它并不是太复杂。
  ShellExecute函数原型及参数含义如下:
  ShellExecute(
  HWND hwnd, //父窗口句柄 (如:NULL,Handle等)
  LPCSTR lpOperation, //操作类型 (如:"open")*要加英文双引号
  LPCSTR lpFile, //要进行操作的文件或路径
  LPCSTR lpParameters, //当lpOperation为“explore”时指定要传递的参数,通常设为NULL
  LPCSTR lpDirectory, //指定默认目录,通常设为NULL
  INT nShowCmd //文件打开的方式,以通常方式还是最大化或最小化显示,一般为SW_SHOWNORMAL
  )
  例子如下:
  //调用计算器
  ShellExecute(NULL,"open","calc.exe",NULL,NULL,SW_SHOWNORMAL);
  //调用记事本
  ShellExecute(NULL,"open","NOTEPAD.EXE",NULL,NULL,SW_SHOWNORMAL);
  ●hWnd:用于指定父窗口句柄。当函数调用过程出现错误时,它将作为Windows消息窗口的父窗口。例如,可以将其设置为应用程序主窗口句柄,即Application.Handle,也可以将其设置为桌面窗口句柄(用GetDesktopWindow函数获得)。
  ●Operation:用于指定要进行的操作。其中“open”操作表示执行由FileName参数指定的程序,或打开由FileName参数指定的文件或文件夹;“print”操作表示打印由FileName参数指定的文件;“explore”操作表示浏览由FileName参数指定的文件夹。当参数设为nil时,表示执行默认操作“open”。
  ●FileName:用于指定要打开的文件名、要执行的程序文件名或要浏览的文件夹名。
  ●Parameters:若FileName参数是一个可执行程序,则此参数指定命令行参数,否则此参数应为nil或PChar(0)。
  ●Directory:用于指定默认目录。
  ●ShowCmd:若FileName参数是一个可执行程序,则此参数指定程序窗口的初始显示方式,否则此参数应设置为0。
  若ShellExecute函数调用成功,则返回值为被执行程序的实例句柄。若返回值小于32,则表示出现错误。
  上述仅仅是ShellExecute函数的标准用法,下面将介绍它的特殊用法。
  2.特殊用法
  如果将FileName参数设置为“http:”协议格式,那么该函数将打开默认浏览器并链接到指定的URL地址。若用户机器中安装了多个浏览器,则该函数将根据Windows 9x/NT注册表中http协议处理程序(Protocols Handler)的设置确定启动哪个浏览器。
  格式一:http://网站域名。
  如:ShellExecute(Handle, "open", http:// ;
  www.neu.edu.cn’, NULL, NULL, SW_SHOWNORMAL);
  格式二:http://网站域名/网页文件名。
  如:ShellExecute(Handle, "open", http:// ;
  www.neu.edu.cn/default.htm',NULL,NULL,
  SW_SHOWNORMAL);
  如果将FileName参数设置为“mailto:”协议格式,那么该函数将启动默认邮件客户程序,如Microsoft Outlook(也包括Microsoft Outlook Express)或Netscape Messanger。若用户机器中安装了多个邮件客户程序,则该函数将根据Windows 9x/NT注册表中mailto协议处理程序的设置确定启动哪个邮件客户程序。
  格式一:mailto:
  如:ShellExecute(Handle,"open", "mailto:", NULL, NULL, SW_SHOWNORMAL);打开新邮件窗口。
  格式二:mailto:用户账号@邮件服务器地址
  如:ShellExecute(Handle, "open"," mailto:who@mail.neu.edu.cn", NULL, NULL, SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址。若指定多个收件人地址,则收件人地址之间必须用分号或逗号分隔开(下同)。
  格式三:mailto:用户账号@邮件服务器地址?subject=邮件主题&body=邮件正文
  如:ShellExecute(handle, ‘open’, ‘ mailto:who@mail.neu.edu.cn?subject=Hello&Body=This is a test’, nil, nil, SW_SHOWNORMAL);打开新邮件窗口,并自动填入收件人地址、邮件主题和邮件正文。若邮件正文包括多行文本,则必须在每行文本之间加入换行转义字符%0a。
  例子(delphi):
  在一个应用程序调用c:\Project1.exe;
  ShellExecute(handle, 'open','c:\Project1.exe','字串内容',nil, SW_SHOWNORMAL);
  在Project1.exe里可以调用:
  procedure TForm1.FormCreate(Sender: TObject);
  var i:integer;
  begin
  for i:=1 to paramcount do
  if ParamStr(i)<>'' then showmessage(ParamStr(i));
  end;
  最后的那个参数,为窗口指定可视性方面的一个命令。
  请用下述任何一个常数
  SW_HIDE 隐藏窗口,活动状态给另一个窗口
  SW_MINIMIZE 最小化窗口,活动状态给另一个窗口
  SW_RESTORE 用原来的大小和位置显示一个窗口,同时令其进入活动状态
  SW_SHOW 用当前的大小和位置显示一个窗口,同时令其进入活动状态
  SW_SHOWMAXIMIZED 最大化窗口,并将其激活
  SW_SHOWMINIMIZED 最小化窗口,并将其激活
  SW_SHOWMINNOACTIVE 最小化一个窗口,同时不改变活动窗口
  SW_SHOWNA 用当前的大小和位置显示一个窗口,不改变活动窗口
  SW_SHOWNOACTIVATE 用最近的大小和位置显示一个窗口,同时不改变活动窗口
  SW_SHOWNORMAL 与SW_RESTORE相同

2009年3月26日星期四

文件查找系统

今天到明天打算做一个文件查找的小程序,刚又去浩哥的博客转了一圈,浩哥你永远是我崇拜的偶像。

是激励我前进的动力。

总之是为了熟悉下MFC的一些界面的函数,为下一步的地图编辑器打个基础。

程序界面大概如下,本来就是模仿别人的小程序。截个图吧。


2008年12月7日星期日

顶级游戏设计大师谈如何成为一名游戏设计师

翻译:wavow   作者:chris crawford ———————————————————————————————————————— 作者简介:Chris Crawford是计算机游戏设计界的元老,至今已经设计、发布了14款值得称道的游戏。 ————————————————————————————————————————

阿,我年轻的朋友们,你们想成为游戏设计师,所以来问我有何好建议?我会给你们我最好的建议,但我猜想你们可能会听不进去。你们可能更愿意听那些说你们爱听的话的人所提供的建议。但这对我来说无所谓,我所能做的就是说些实事,并希望能进入某些人的耳朵里。   首先,你们应该对自己的事业道路有个最初的规划,你想接受更专门的训练,还是想要更全面的教育?专门的训练能给你某些特别的技能,让你能一毕业就进入工作。但教育能给你一个更全面的素质培养,却不一定能马上看到好处,不过长期来看能给你带来更多的优势。这是个简单不过的选择:速成的路还是战略规划后的路。如果你非常急于求成,那就立马去那些专门学校,他们会教你最新最炫的电脑技术。精力充沛是年轻人的优势,但耐性决不是年轻人的特长,所以我完全能理解你们几乎不能忍受去学习那些看似无关的副科。当我还是你们年纪的时候,我总是对那些大学里的其他课程感到不耐烦。但现在,我对我当初的鲁莽想法感到羞愧,并真心的感谢当初教导我的师长。
  速成的路却是能立刻见效的路。如果你进了一家专门教授电脑游戏制作的学校,或者在正规大学主修电脑游戏设计。在此,你会学到很多关于电脑游戏设计最新的技术。毕业后,你很可能马上就能在正规的游戏公司找到一份工作,而这一切你可以在23岁之前搞定。
  请打住。在这里有一点需要指出。生产游戏和设计游戏是完全不同的。你所获得的第一份工作肯定是份渺小的工作,比如给一个无关紧要的npc画动画,而这个npc做的事情只是在背景上漫无目的的闲晃;或者写一段代码,这段代码的用处就是当用户按下exit的时候,显示“你确定要退出么?”。如果你很擅长做这些小事情,那不出几年就可能会让你画些更复杂的动画或写一段更重要的代码。然后再过几年,你就可能坐上某个更重要的位子,处理些更重要的事情。
  但请你别指望这些。根本的问题在于:有成百上千甚至上百万的学生,就像你们一样的学生,正做着同样的梦,想要进游戏业的梦。考虑一下供求曲线吧,如果职员的供应量十倍甚至百倍于员工的需求量,那么价格曲线将向下倾斜。你们必定将得到可怜的工资,并得不到任何的尊敬。你们当然可以抱怨,但得到的回答不仅简单也很实在:如果你不喜欢,你可以选择离开阿,门外有上百个像你一样的年轻人想要得到你的工作。
  事实上,这就是当前正在发生的事情。有可能的话你们可以去每年三月或四月在圣何塞举行的“游戏开发者大会”看看,你们并不需要真的去参加什么讨论会,只需要在圣何塞的会议中心转转,看看身边走过的人。你会发现两个惊人的事实:第一,每个人都穿着黑色衣服;第二,他们的平均年龄在25至30之间。
  我不知道为什么他们都穿着黑色,这可能是每个人都需要遵守的规则。但我能告诉你们,为什么他们都这么年轻:因为每个人都会在工作几年之后离开这个行业。游戏业就像是个奇怪的巨大建筑物,这个建筑物只有一个入口,却有很多个出口。成千上万个年轻人拥挤在入口处,争先恐后互相推挤着想要进门。只有很少一部分能顺利进入。但对于任何一个进入者来说,总会有另一个人离开——这才能让这个行业保持平衡。这个行业内的人有多么的年轻,就意味着被挤出去的人有多么的快。很少有人能撑到30岁。
  如果你想得再深些,那就会发现另一个事实:假设你是老板,既然有这么多小家伙想要为你做游戏,那你自然可以花一毛钱就雇一群人,像奴隶一样驱使他们,直到他们筋疲力尽,然后再找些替代者。你只需雇佣一些管理人员,能保证这些小家伙都努力工作就行。这个系统运转得非常完美。
  唯一的问题是,你是否打算成为他们中的一员呢?我希望不是。不过,要是你真狂热到打算削尖脑袋往游戏业里钻,那么就继续前进!我这个老笨蛋说再多也是无益。你自己去花时间看清这些吧。
  但在这里我还有另一个方案可以告诉你们。首先,你应该获得一个正规的教育,决不是什么一夜情式的培训。去一所正规的大学,学习任何专业除了游戏制作。几乎所有专业都可以:生物学、物理学(我当初就是念的这个)、艺术、文学、历史、心理学、语言学。你只需保证自己获得了常规教育,并尽量选修一些专业以外的课程,当然,你应该选修一下计算机。
同时,你应该开始试验制作游戏了。不要被时髦流行的图像技术所扰——这不能给你的游戏设计提供任何帮助。你应该专注于游戏的内涵:游戏的体系和架构。如何用很少的元素就能使整个游戏运转?别幻想能做出像商业游戏那么好的产品——我要大声告诉你,这些游戏要依*很多人的力量才能完成。你此时所能做的这些小玩意同那些比较起来,只能用“可怜”两个字形容。要是把制作游戏比作造汽车,此时你根本不用管车子的外表涂层和喷漆,你应该专注于如何让活塞协作运转、如何控制阀门的正常闭合、搞清楚汽化器是个什么东西。你要制造的是个小卡丁车,而不是一辆劳斯莱斯。所有这些都是个试验的过程,别幻想你的这些小玩意会有商业用途,你要做的就是造好一个就扔掉一个——为了增加创造力,你要杀掉你自己的“孩子”。如果你沉迷于这些作品而不愿放弃,那你将永远也不会拥有一个真正游戏设计师所应有的创造力。
  继续不断的为增加你的创造力而努力。到目前为止,你的作品根本无法和一个经验老到的游戏设计师相抗衡,所以增加自己的能力是你此时唯一该做的。电影the matrix都看过吧,没经过训练的neo无论如何也不是agent smith的对手。学任何你能学到的东西。毕业前要把你学校图书馆所有的书架都翻一遍,在这些灰尘的覆盖下指不定隐藏着什么有趣的东西。
  当你出了学校,不要马上就进入游戏业。在一个正规公司找一份像样的工作,并挣些钱。更主要的是不断学习,你要学很多关于团队协作的方式,也要学会如何在众人面前寻找自己的位置。你应该学会何时何地站起来对你的老板说话——虽然这种机会不常有。还有,你应该学会如何同周围有经验的内行打交道。
  继续在你业余时间制作游戏,尽可能多的制作各种不同类型的“小卡丁车”,并试图了解各种类型卡丁车的性能,诸如操纵性、速度以及其他要素。当你做了六个或十个这种产品,你可能会想到继续亲自着手进行更大的项目。很好,去找些志同道合的朋友,他们会帮助你完成你的计划,我确信这将是个令人印象深刻的成果。完成之后,向全世界展示你的小宝贝吧。现在,你就可以把她作为你简历的一部分去申请游戏业中的职位了。如果你的那些产品确实好,你将能得到一个真正的游戏设计师职位,而不是去当那些小喽罗。此时你可能仍然只是其他游戏设计师的助手,但你已经处在了正确的位置上,如果你能努力出色的完成工作,你肯定会在游戏业中拥有前途。
  我知道这些并不是你们想听的内容。你们想要的是所谓的速成药,比如学习这个,然后学习那个,接下来就能确保得到一份高收入的工作、拥有巨大的办公室、用着最先进的电脑设备、做的是富有创造性的事情。没错,每个人都会这么想的,但没有人会得到这些。那些向你描述这些故事的人不过是些骗子,他们脑子里想的只是你口袋里的钱。一个令人悲伤的事实是:游戏先驱们主导世界的日子已经过去了,现在的游戏业是一个产业,没人会突然被发掘然后一夜之间成为巨星,对任何想入门的人来说,在你面前的必定是一条漫长的路。
  你们拥有激情和精力,想要让不可能变为可能。但你们是否对这条漫长的路做好了准备?或者说,你们是否打算在真正的准备完成前就冲进去?
  祝你们好运,孩子们,我相信你们会成功。

-------------------------------------------------第2话题--------------------------------------------------

汤姆·霍尔的快速成为游戏开发者的秘诀

“离子风暴”开发公司的奠基人之一汤姆·霍尔先生在网上发表了一篇有趣的文章,披露了快速成为游戏开发者的“成功秘诀”。唉,世上哪有那么便宜的事呢?可汤姆·霍尔说得一本正经,一脸严肃,不由得你不信。再加上笔者生来对“投机取巧”“不劳而获”“一蹴而就”之类的事情有一种按捺不住的好奇(首先声明,笔者一生的道路走得踏踏实实,一步一个脚印,绝无此类“劣迹”,仅好奇耳),便偷偷地看了。不看则已,一看才知世上竟有汤姆·霍尔这类大滑头,失望之余便有些忿忿然,忿忿然之余又不禁哑然失笑,豁然明白了霍尔先生的苦心。   

原来他出此奇文并非存心戏弄读者,实在是有几分“劝世”的用意在其中。作为闻名遐迩的游戏开发者,汤姆·霍尔在开发游戏的同时(眼下他正忙着开发一款以未来为背景的游戏:《时空异变》),常常会收到一些来信,写信者表露出有心在游戏业驰骋一番的雄心,提出的问题可大致划分为以下三种:

  1、“我脑子里新奇念头层出不穷,你愿意雇用我吗?”  

对于第一种来信,霍尔的回答只有两句话:“新奇念头人人有”和“不行”。霍尔解释说:“如今游戏业并不缺乏新奇念头,每天都有成千上万的新念头在产生和消亡。尽管新奇对于一个游戏的成败至关重要,然而更重要的是行动,将其付诸实现。爱迪生说什么来着:‘发明是百分之一的灵感和百分之九十九的汗水的混合物。’除非你拿出东西来,否则没人愿意听你凭空说话。埋头制作游戏需要某种献身精神,你可能会嘲笑那些开发游戏的人‘这也做得不好’、‘那也做得不对’,可他们实实在在完成了一件作品,这就使他们显得与众不同。因为99%的人是希望在他们付出劳动之前能确保得到回报的庸常之辈。”  

2、“我把自己刚刚完成的游戏设计寄来,你能抽空看看吗?很了不起的构想噢!”  

对于这种来信,霍尔拒绝得更快,几乎是不加思索的“No”。原因也有些出人意表:“他们寄来的构想可能是粗浅的习作,也可能是未来的惊世大作,可我是一眼也不敢看的。原因很简单,我自己也是搞游戏策划的,他的想法可能会与我已有的想法不谋而合。由于这种事无法证明,所以他会认为我剽窃了他的念头。第二,我看过的东西会残存在我的感知里,在我将来的游戏策划中它会不知不觉地跑出来作怪,这样我还是摆脱不了被指控剽窃的危险。第三,一旦我看过了某个构思,我就必须对其加以评论,运用我自己的想法对其进行指导。出于自私的考虑,我不想让自己的‘酷’念头为他人所用,我自己将来还用得着呢。还有呵,对于杂志上发表的有关游戏策划的评说我也是避之而唯恐不及,尽管发表意见的人会说:‘这就是我认为的好游戏,你们就照着做吧。’可一旦这个念头被某个游戏开发者所采用,并且挣了几百万,98%的人会打官司索要专利费。另外2%的人也会在配偶的撺掇下把你告上法庭。唯一的办法就是眼不见为净。”  

3、“我觉得制作游戏是一件令人兴奋的事。你是怎么走上这条路的?你使用了什么工具?”  

让霍尔先生看着顺眼的大概只有这第三种问题了。由于这种信件仅仅是提出问题,而没有隐含的危险性,霍尔先生的解答也就来得很爽快。在这里他不仅仅是回答问题,而是借此对游戏开发这个行当表述了自己独特的见解。  “

倘若你真的喜欢电脑游戏,而你周围又没人会开发游戏,而你自己又没有通过书本和工具学会开发游戏的能力,除非有人教你如何开发游戏否则你便不知该怎样着手,倘若情况是这样,那你就趁早打消开发游戏的念头。   

“倘若你刚刚玩过一款伟大的游戏便觉得自己也能行,而实际上你对游戏开发一无所知,你所持有的仅仅是对成功和名望的渴望,那你就不要做游戏开发者。  “倘若你无可救药地迷上了电脑游戏,如醉如痴地阅读游戏杂志,时不时地用Quick BASIC编写自己的简单游戏,不畏艰难地将一些好游戏的内核打开,来回比较它们的优劣,那你也不适合搞游戏开发。”  

汤姆·霍尔不留情面地把以上几种人都“炒了鱿鱼”,因为在他看来,游戏业不是个仅靠热情和痴迷便能胜任的行当。游戏公司是赚钱的公司,而不是搞游戏研究的地方。这里的竞争法则是严酷的,一如好莱坞电影业。在投奔这个新世界之前你要考虑再三,并且要准备在开始阶段去饭馆端碟子 — 可能会端上一辈子。  “倘若以上所说的仍难不住你,你仍旧无法打消开发游戏的念头,那么我就给你详细描述一下你将面临的艰难道路,这是一条可能走向成功的充满荆棘的道路。”看来霍尔先生终于不忍心将那么多痴迷的年轻人拒之门外,开始布道施教了。“在你充分了解了它的艰难之后,我将向你指出另一条路,我会把不必花费多少力气便能快速成功的超级秘诀告诉你。不过要在你阅读了下面的内容之后。”  

打开你的电脑,在DOS状态下敲入QBASIC,你会发现有一个程序在运行。它就是QBASIC,随着DOS附送的一个编程程序。你的操作系统种没有一样东西是用它编写的 — 它运行太慢 — 但你却可以用它来写一些游戏程序。依照霍尔先生的看法,你应该先从学习别人的程序着手。上街买一些游戏编程手册,然后就可以干起来了。你会犯这样或那样的错误,然而没人会在意的。在“观摩学习”中,你要留意几个问题:他们是如何使游戏变得有趣而使操作变得简单的?倘若换了自己会怎么做?他们是如何改进的?什么东西使你在玩了50个小时之后依然不对它产生厌烦?  

汤姆·霍尔布置的作业可不轻松,大约需要开发100个左右自己的小游戏。前10个应该是不堪入目的入门习作,从第11个开始,应该有了一点起色,从第50个游戏起,你开始有了自己的声音。正如音乐家的成长一样,在你开发了100个游戏之后,游戏开发程序就成了可以让你任意支配的乐器,用它来随心所欲地表达自己的思想了。  

你要开发的游戏种类应该没有限制,想到什么就开发什么。作为开始,你不妨拿游戏业最老的游戏之一:Pac - Man来作临本,你会发现它实际上不象看上去那么简单。你还可以让一个小人从A点走到B点,躲避从C点掉下来的一个小球。关键是你要开始制作游戏,并完成它。随着自己的作品的增多,你会产生自豪感,并且会逐渐感到上瘾。等到某一天,你的朋友要求拷贝一份你制作的游戏回家去玩,那时你差不多就出徒了。你再绞尽脑汁制作一个全新的游戏,一个前人从未干过的新品,尽可能把它打磨精细,然后就可以拿到游戏业界去闯运气了。  成功的道路不止一条,然而汤姆·霍尔走向成功的道路却是实实在在的先例,想成为游戏开发者的人自然很想知道。我们将他走过的道路粗略地画了一个轮廓:  从小常去玩街机,后来自己买了个游戏机,上高中时买了电脑,自己开发了50个游戏。而后上大学学习编程,又利用业余时间制作了30个游戏,其它的业余时间则全部用来玩游戏。阅读有关书籍与杂志的同时,开始为杂志撰稿,还为有智力障碍的学生编制游戏,毕业时拿到了计算机专业学位,在一个小公司谋得职业,它每月都有程序发布。在搞应用程序的同时又制作了几款游戏,并开始在其它一些制作游戏的部门窥探,结识了几个游戏天才人物,并使自己的作品得到他们的赏识。等到羽毛丰满,便自立门户,创建了自己的游戏公司。每天工作16个小时,每星期工作7天,挣的钱仅够维持生计。制作各种各样的游戏,逐步走向成功。这条道路是艰难和漫长的,然而要想成为一个游戏开发者,该做的事一样少不了。  

“不是说还有一条道路吗?”心有不甘的读者问。  

不错,的确还有一条路。走这条路你可以不必花费时间和精力去学习编程,你可以在一夜之间实现自己的梦想。汤姆·霍尔知道这条路,并把它称为“走向成功的超级秘诀”,慷慨地在此奉献给大家。秘诀分为三个步骤,排列如下:  

第一步:有几百万美金闲钱可调用。  

第二步:购买一个现成的游戏开发公司。  

第三步:别忘了在合同里加上一条,公司开发的所有游戏前面都要加上你的大名。  

大功告成。这样你就(至少在公众的眼里)成了一位游戏开发者。是不是简洁而又迅速?看着游戏封面你那烫金的大名,你是否感到陶醉?唯一的不足之处就是,你别指望这样别人会对你产生多少敬意。假如你真的想成为货真价实的游戏开发者,除了第一条充满荆棘的困难道路外,似乎没有什么别的路好走。

2008年11月29日星期六

关于程序设计

在大学里面,要做出东西来,那么就要先天准备一下基础知识。
        UI是基础,不管做什么,都要先掌握一套UI系统,然后便是针对你要做的东西所要掌握的SDK函数,设计模式,对象的设计,基本算法的掌握。基本上就是这样。
windows程序的话,MFC,C#windows form界面都可以,游戏的话也一样,先要掌握一套UI系统,感觉游戏对UI的要求不是很强,windows应用程序就不同了,UI界面便是整个程序的框架,必须把它掌握了。
游戏掌握了UI之后,在掌握一套游戏引擎,那么再看看设计模式。
在游戏的世界里,我便是神。