C++面向对象游戏模型

简单模拟一下“英雄联盟”的模型,先把所有英雄抽象成Hero,他们有很多共同的属性和攻击特性,所以先定义一个抽象类。

由于注释已经写得很清楚了,所以也无需赘述。

#ifndef __HERO__
#define __HERO__

#define TRUE		1
#define FALSE		0

typedef enum ability {
	ABILITY_Q = 0x0000000a,
	ABILITY_W = 0x0000000b,
	ABILITY_E = 0x0000000c,
	ABILITY_R = 0x0000000d } ABILITY; //Q,W,E,R技能枚举

typedef int	HEROPARAM;
typedef int	BOOL;

//英雄抽象类
class Hero
{
protected:
	HEROPARAM _level;						//等级
	HEROPARAM _experience;					//经验值
	BOOL _died;								//是否死亡

	/*消耗系列参数*/
	HEROPARAM _max_health;					//最大生命值
	HEROPARAM _cur_health;					//当前生命值
	HEROPARAM _health_up_speed;				//生命恢复速度(点/每秒)
	HEROPARAM _max_mana;					//最大法力值
	HEROPARAM _cur_mana;					//当前法力值
	HEROPARAM _mana_up_speed;				//法力恢复速度(点/每秒)

	/*攻击系列参数*/
	const HEROPARAM _EFFECTIVE_PER_LEVEL;	//每级增长攻击力(点/每级)
	HEROPARAM _effective;					//当前攻击力
	HEROPARAM _effective_penetration;		//当前攻击穿透
	HEROPARAM _crit;						//暴击几率(0 to 100)
	HEROPARAM _magic;						//当前法术强度
	HEROPARAM _magic_penetration;			//当前法术穿透

	/*防御系列参数*/
	const HEROPARAM _ARMOR_PER_LEVEL;		//每级增长护甲(点/每级)
	HEROPARAM _armor;						//当前护甲
	HEROPARAM _resistance;					//当前魔法抗性

public:
	Hero(HEROPARAM max_health,
		HEROPARAM health_up_speed,
		HEROPARAM max_mana,
		HEROPARAM mana_up_speed,
		const HEROPARAM effective_per_level,
		HEROPARAM effective,
		HEROPARAM effective_penetration,
		HEROPARAM crit,
		HEROPARAM magic,
		HEROPARAM magic_penetration,
		const HEROPARAM armor_per_level,
		HEROPARAM armor,
		HEROPARAM resistance);	//构造一个英雄
	virtual ~Hero();													//析构英雄
	virtual void attack(Hero *destHero);								//普通攻击目标英雄
	virtual void excute(Hero *destHero, ABILITY what) = 0;				//对目标英雄释放技能(what是指定技能,Q,W,E,R四个技能)
	virtual HEROPARAM getEffective();									//获取攻击力
	virtual HEROPARAM getArmor();										//获取护甲值
	virtual HEROPARAM getEffectivePenetration();						//获取护甲穿透值
	virtual HEROPARAM getCurHealth();									//获取当前生命值
	virtual void putCurHealth(HEROPARAM cur_health);					//设置当前生命值
	virtual void die(Hero *srcHero);									//死亡(被某个英雄击杀)
	virtual const char* getSelfName(char *name) = 0;					//获取英雄自己的名字
	virtual HEROPARAM getMagic();										//获得法术强度
	virtual HEROPARAM getMagicPenetration();							//获得法术穿透
	virtual HEROPARAM getResistance();									//获得魔法抗性
};

#endif

接下来把这个抽象类中的非纯虚函数实现

#include "stdafx.h"
#include "Hero.h"

Hero::Hero(HEROPARAM max_health,
	HEROPARAM health_up_speed,
	HEROPARAM max_mana,
	HEROPARAM mana_up_speed,
	const HEROPARAM effective_per_level,
	HEROPARAM effective,
	HEROPARAM effective_penetration,
	HEROPARAM crit,
	HEROPARAM magic,
	HEROPARAM magic_penetration,
	const HEROPARAM armor_per_level,
	HEROPARAM armor,
	HEROPARAM resistance) :
		_EFFECTIVE_PER_LEVEL(effective_per_level),
		_ARMOR_PER_LEVEL(armor_per_level)
{
	_level = 1;
	_experience = 0;
	_died = FALSE;
	_max_health = max_health;
	_cur_health = max_health;
	_health_up_speed = health_up_speed;
	_max_mana = max_mana;
	_cur_mana = max_mana;
	_mana_up_speed = mana_up_speed;
	_effective = effective;
	_effective_penetration = effective_penetration;
	_crit = crit;
	_magic = magic;
	_magic_penetration = magic_penetration;
	_armor = armor;
	_resistance = resistance;
}

Hero::~Hero()
{
}

void Hero::attack(Hero *destHero)
{
	HEROPARAM armor = destHero->getArmor() - this->getEffectivePenetration(); //减去护甲穿透之后的护甲
	armor = armor > 0 ? armor : 0; //护甲不能为负数
	HEROPARAM effective = this->getEffective();
	BOOL bIsCrited = FALSE;
	SYSTEMTIME st;
	GetLocalTime(&st);
	srand((unsigned)st.wMilliseconds);
	int num = rand() % (100 - 1) + 1; //产生1到100到随机数
	if (num <= _crit) //决定是否暴击,看看产生的随机数是否小于暴击几率,暴击几率越高,此条件越容易成立
	{
		effective <<= 1; //暴击
		bIsCrited = TRUE;
	}
	HEROPARAM lost_health = (int)((float)effective * (100 / (100 + (float)armor))); //目标损失的生命值,实际对你造成的物理伤害=自己攻击力*[100/(100+对方护甲值)]
	HEROPARAM cur_health = destHero->getCurHealth() - lost_health; //所剩生命值
	char name[255];
	destHero->getSelfName(name); //得到目标英雄名字
	if (bIsCrited)
	{
		printf("你普通攻击了【%s】,并且暴击了!\n", name);
	}
	else
	{
		printf("你普通攻击了【%s】。\n", name);
	}
	if (cur_health <= 0)
	{
		destHero->die(this); //生命值小于或等于0就死亡
		printf("你击杀了【%s】!\n", name);
		_experience += 50; //击杀英雄加50点经验
		//经验值到100就升级
		if (_experience >= 100)
		{
			_experience = 0;
			printf("你升级了!当前等级是%d。\n", _level);
		}
	}
	else
	{
		destHero->putCurHealth(cur_health); //没死就减血
		printf("【%s】丢失%d生命值,剩余生命值%d。\n", name, lost_health, cur_health);
	}
}

HEROPARAM Hero::getEffective()
{
	return _effective;
}

HEROPARAM Hero::getArmor()
{
	return _armor;
}

HEROPARAM Hero::getEffectivePenetration()
{
	return _effective_penetration;
}

HEROPARAM Hero::getCurHealth()
{
	return _cur_health;
}

void Hero::putCurHealth(HEROPARAM cur_health)
{
	_cur_health = cur_health;
}

void Hero::die(Hero *srcHero)
{
	char name[255];
	srcHero->getSelfName(name);
//	printf("你被【%s】击杀了!\n", name);
}

HEROPARAM Hero::getMagic()
{
	return _magic;
}

HEROPARAM Hero::getMagicPenetration()
{
	return _magic_penetration;
}

HEROPARAM Hero::getResistance()
{
	return _resistance;
}

我这样计算暴击几率也不知道合适不合适,因为这样每次暴击是否触发都是独立事件,也就是说,如果运气特别好,可能每次都暴击,当然我相信不会有人运气这么好的!

这样,就定义并实现了Hero这个抽象类,然后要做的就是从这个类中派生出具体的英雄,比如现在派生一个Ashe(艾希)类

头文件:

#ifndef __ASHE__
#define __ASHE__

#include "Hero.h"

//寒冰射手-艾希
class Ashe : public Hero
{
public:
	Ashe(HEROPARAM max_health,
		HEROPARAM health_up_speed,
		HEROPARAM max_mana,
		HEROPARAM mana_up_speed,
		const HEROPARAM effective_per_level,
		HEROPARAM effective,
		HEROPARAM effective_penetration,
		HEROPARAM crit,
		HEROPARAM magic,
		HEROPARAM magic_penetration,
		const HEROPARAM armor_per_level,
		HEROPARAM armor,
		HEROPARAM resistance);	//构造一个艾希
	virtual void excute(Hero *destHero, ABILITY what);
	virtual const char* getSelfName(char *name);
};

#endif

源文件:

#include "stdafx.h"
#include "Ashe.h"

Ashe::Ashe(HEROPARAM max_health,
	HEROPARAM health_up_speed,
	HEROPARAM max_mana,
	HEROPARAM mana_up_speed,
	const HEROPARAM effective_per_level,
	HEROPARAM effective,
	HEROPARAM effective_penetration,
	HEROPARAM crit,
	HEROPARAM magic,
	HEROPARAM magic_penetration,
	const HEROPARAM armor_per_level,
	HEROPARAM armor,
	HEROPARAM resistance) :
		Hero(max_health,
			health_up_speed,
			max_mana,
			mana_up_speed,
			effective_per_level,
			effective,
			effective_penetration,
			crit,
			magic,
			magic_penetration,
			armor_per_level,
			armor,
			resistance)
{
}

void Ashe::excute(Hero *destHero, ABILITY what)
{
	char name[255];
	destHero->getSelfName(name);

	if (ABILITY_Q == what)
	{
		printf("[寒冰射击]使【%s】减速了。\n", name);
	}
	else if (ABILITY_W == what) //W技能对目标造成40+1全额攻击力的物理伤害
	{
		if (_cur_mana < 60)
		{
			return;
		}
		_cur_mana -= 60; //W技能消耗60点法力值
		HEROPARAM armor = destHero->getArmor() - this->getEffectivePenetration();
		armor = armor > 0 ? armor : 0; //护甲不能为负数
		HEROPARAM lost_health = (int)((40 + (float)this->getEffective()) * (100 / (100 + (float)armor))); //目标损失的生命值,实际对你造成的物理伤害=100/(100+护甲值)
		HEROPARAM cur_health = destHero->getCurHealth() - lost_health; //所剩生命值
		printf("[万箭齐发]使【%s】减速了。\n", name);
		if (cur_health <= 0)
		{
			destHero->die(this); //生命值小于或等于0就死亡
			printf("你击杀了【%s】!\n", name);
			//经验值到100就升级
			if (_experience >= 100)
			{
				_experience = 0;
				printf("你升级了!当前等级是%d。\n", _level);
			}
		}
		else
		{
			destHero->putCurHealth(cur_health); //没死就减血
			printf("【%s】丢失%d生命值,剩余生命值%d。\n", name, lost_health, cur_health);
		}
	}
	else if (ABILITY_E == what)
	{
		printf("[鹰击长空]刚好照亮了【%s】。\n", name);
	}
	else if (ABILITY_R == what) //R技能对目标造成250+1全额法术强度的魔法伤害
	{
		if (_cur_mana < 100)
		{
			return;
		}
		_cur_mana -= 100; //R技能消耗100点法力值
		HEROPARAM resistance = destHero->getResistance() - this->getMagic(); //减去法术穿透后的魔法抗性
		resistance = resistance > 0 ? resistance : 0; //魔法抗性不能为负数
		HEROPARAM lost_health = (int)((250 + (float)this->getMagic()) * (100 / (100 + (float)resistance))); //目标损失的生命值,实际对你造成的魔法伤害=100/(100+魔法抗性)
		HEROPARAM cur_health = destHero->getCurHealth() - lost_health; //所剩生命值
		printf("[魔法水晶剑]命中了【%s】。\n", name);
		if (cur_health <= 0)
		{
			destHero->die(this); //生命值小于或等于0就死亡
			printf("你击杀了【%s】!\n", name);
			if (_experience >= 100)
			{
				_experience = 0;
				printf("你升级了!当前等级是%d。\n", _level);
			}
		}
		else
		{
			destHero->putCurHealth(cur_health); //没死就减血
			printf("【%s】丢失%d生命值,剩余生命值%d。\n", name, lost_health, cur_health);
		}
	}
}

const char* Ashe::getSelfName(char *name)
{
	return strcpy(name, "寒冰射手");
}

Ashe这个类的构造函数就直接调用父类的构造函数来构造了,因为所有的英雄基本属性和攻击特性都是类似的,只是数值不同而已。

最后,构造2个艾希,演示一号艾希攻击二号艾希

// LOLModel.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Ashe.h"

int main(int argc, char* argv[])
{
	Hero *ashe1 = new Ashe(528, 5, 234, 7, 3, 51, 0, 55, 0, 0, 3, 22, 30); //带暴击几率符文的艾希,假设55%暴击
	Hero *ashe2 = new Ashe(528, 5, 234, 7, 3, 80, 0, 0, 0, 0, 3, 32, 40); //普通符文的艾希
	ashe1->attack(ashe2);
	Sleep(1000);
	ashe1->excute(ashe2, ABILITY_W);
	Sleep(1000);
	ashe1->excute(ashe2, ABILITY_R);
	Sleep(1000);
	ashe1->attack(ashe2);
	Sleep(1000);
	ashe1->attack(ashe2);
	Sleep(1000);
	ashe1->attack(ashe2);
	Sleep(1000);
	ashe1->attack(ashe2);
	Sleep(1000);
	ashe1->excute(ashe2, ABILITY_W);
	delete ashe1;
	delete ashe2;
	return 0;
}

用父类的指针来指向具体英雄的对象,通过多态的方式来调用子类对象的函数。

其实还有很多因素没有考虑到,比如目标已经死亡了还是可以攻击,再比如生命和法力回复就没有考虑。如果实在要考虑的话,可以新开一个1秒触发一次的定时器,每次触发遍历所有对象,给这些对象加上相应的值。没有完善的太多了,反正就这样了。。。

时间: 2024-08-27 10:24:32

C++面向对象游戏模型的相关文章

《逐梦旅程 WINDOWS游戏编程之从零开始》笔记10——三维天空的构建&amp;三维粒子的实现&amp;多游戏模型的载入

第23章 三维天空的构建 目前描述三维天空的技术主要包括三种类型,直接来介绍使用最广泛的模拟技术,详细的描述可以见作者的博文. 天空盒(Sky Box),即放到场景的是一个立方体.它是目前使用最广泛的三维天空模拟技术,网络上素材丰富,所以这次就用教大家用天空盒来模拟三维天空.天空盒经常是由24个顶点.六个面组成的立方体(或者直接从做好的X模型文件载入天空盒),并经常会随着视点的移动而移动,来刻画极远处玩家无法达到位置的天空 天空盒的设计 1.准备天空盒纹理素材 天空盒的纹理自然就是我们这个天空盒

3d游戏模型及地形提取及导航

支持所有DirectX的游戏模型提取 有需要的可以直接联系我!QQ290387340

游戏模型3Ds max《战神》人物制作解析

游戏美术行情在线 首先看下这个作品的最终效果,如下图.(图01) 图01 在网上找些战神参考图片,这个环节很重要,需要观察角色的每个细节,包括面部表情等.(图02) 图02 游戏低模制作 由于参考图有限,有些模型比例上需要自己来把握下.我是选择在3ds Max里面先建个初始模型,再去ZB里调整比例和大型.模型的头部是使用3ds Max的头部生成器制作的,身体部分的制作有很多方法来制作.需要注意的是尽量保持四边面,这样有利于ZB细分雕刻. 图03 游戏模型ZB雕刻 人物雕刻 在ZB里根据参考图雕刻

游戏模型:场景布局及场景建模

今天卡拉维德专业游戏美术学习交流平台约大家分享的是CG游戏的场景布局及游戏建模,具体流程如下所示.了解如何使用3ds Max和Marmoset创建一个世界末日的立体模型,这是您建模技能的完美组合展示.本教程将指导您完成创建diorama的过程,您将能够在Web浏览器中进行交互. 步骤1:基本地形简单游戏建模这里有四个独立的网格,构成了环境的地板. 我们将做一个越野的地形在柏油路面,路边,路面和杂草丛生的草地. 我使用简单的盒子建模技术来创建这些网格,他们是相当低的res,因为我想要的材料,纹理和

【游戏模型】游戏超写实贴图制作技巧

http://blog.sina.com.cn/s/blog_8ccf42ae0101bfcv.html 一 基础分析 金属类贴图可以说在常规贴图里占着很大的分量,是我们很常用而且比较容易出效果的贴图之一.金属的种类虽然多,但是我们的表现手段是有限的,这样我们就必须要有清晰的思路来表现props金属特性. 归类了一下,我们制作时常表现的金属特性. A. 冷暖.轻重金属.在我们制作中,一些比较高科技感觉的仪器或空间,都常选择比较遍冷色调的金属.然后,以轻金属比例较大,没有太脏的锈迹,质感比较圆滑.

Java GUI 简单台球游戏模型

完成效果: 1 package com.neuedu.test; 2 3 import java.awt.Frame; 4 import java.awt.Graphics; 5 import java.awt.event.WindowAdapter; 6 import java.awt.event.WindowEvent; 7 8 import com.neuedu.utill.GameUtill; 9 10 public class TaiQiu extends Frame { 11 pub

游戏为模型的软件架构续

Player, NPC, Map, Director, Controller. 这里map交给谁来做,游戏里是场景设计手,从场景考虑的多方民协调来看有些像给内架构的任务.在游戏制作里,map是比较重要的文化核心,影响了出现在上边的怪和NPC,影响了将要形成的路线和可提供的服务. 建模为了解决问题,不一定要锁定到模型本身,可以变化着适应需求.可我觉得这里的map并不能随便改掉.每个map代表着一项服务集合.这个集合的构成应该是有思考的.每个map像是一个服务器,同一个服务器里放什么东西比较不好分配

Unity3D游戏开发从零单排(五) - 导入CS模型到Unity3D

游戏动画基础 Animation组件 Animation组件是对于老的动画系统来说的. 老的动画形同对应的动画就是clip,每个运动都是一段单独的动画,使用Play()或CrossFade(),直接播放动画 或淡入淡出播放动画. animation.Play("name"); animation.CrossFade("name"); 下面的是它的几个属性 Animation:默认的动画片段: Aniamtions:包含的动画片段: Play Automaticall

游戏贴图与模型基本规范

Q 经常看见模型要求多少多少面这样的制作要求,那么你们是怎样计算面数的?A 通常我们提到游戏模型的面数都是指的三角面,而通常在你在3D软件中所看到它计算的面数都是四边面.一个四边面等于两个三角面,但并不是说你想了解你的模型有多少面就直接将你看到面数*2就行了.因为如果你的模型中还有三角面的结构甚至大于四条边的结构(当然这是不允许的),你所计算的面数就会有所出入.所以保险的办法还是将你的模型先用工具转换为全三角结构后再看他的面数.在3DSMAX中你只需要很简单的将他塌陷为Editable Mesh