深圳大学计算机游戏开发实验3 二维游戏动画合成

本文详细记录了一次Cocos2d-x游戏实验的过程,包括游戏编译、计分板功能和回合制的实现,英雄防御动作动画设计,以及游戏中的多个bug修复。实验者还对游戏进行了优化,如增加无敌帧、改进敌人AI和添加背景音乐。此外,文章讨论了实验中遇到的问题和解决方案,展示了对游戏开发技术的深入理解和应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、实验目的与要求

二、实验内容与方法

三、实验步骤与过程

1. 完成游戏编译

2. 增加计分板功能,并将游戏改为回合制游戏

 3. 设计英雄Defend动作动画

4. 将动画添加到游戏中

 5. 查找和修改游戏Bug

Bug1:如下图所示,出现两个敌人的血条且不重合。

Bug2:英雄角色受击后无法操作,而且没有执行受击动作。

Bug3:点击攻击按钮,英雄和敌人同时响应攻击操作。

Bug4:第二回合开始时,按下攻击按钮英雄不执行攻击动作了。

Bug5:连续点击攻击按钮时,攻击动作没有执行完毕就开始执行新的攻击动作。

Bug6:英雄和敌人的攻击击中对方时,攻击动画未结束就进行下一个动作。

Bug7:云朵移动动画出现衔接不上的现象。

Bug8:敌人大概率向左走,最后会撞左边的墙。

Bug9:攻击动作的碰撞检测出现问题,有时候会出现没有打到对方但掉血的现象

6. 优化游戏

优化1:给角色添加无敌帧

优化2:优化敌人AI

优化3:添加游戏音乐

四、实验结论或心得体会


前言

这门课的所有实验都挺简单的,但是我估摸着混了个A+,不写白不写,本次实验可能会有我没有发现的错误或者仍需完善的内容,但整体上是没有问题的,得分也不低。

一、实验目的与要求

1.了解二维游戏动画合成原理。

2.熟悉Cocos2d-x中的用户交互、触摸事件、碰撞检测机制。

3.熟悉CocoStudio动画编辑器的使用,了解骨骼动画。

二、实验内容与方法

1.完成游戏编译

成功编译并运行教材P128“游戏动画实例-侠客行”。

2.增加计分板功能和回合制 

记录成功/失败次数,增加计分板功能;将游戏改为回合制游戏。

3.增加英雄Defend动作

利用CocoStudio设计英雄Defend动作,并将此功能在游戏中加载。

4.修改游戏bug

修改游戏中出现的明显bug。

5. 游戏优化升级

自行发挥想象力,优化游戏功能。

三、实验步骤与过程

1. 完成游戏编译

创建游戏项目,将窗口大小改为960x640,详细步骤见我的实验1的文章。

2. 增加计分板功能,并将游戏改为回合制游戏

(1)在AnimationScene.h中新声明两个变量。分别记录英雄和敌人赢的回合数。

	//得分
	 int goal_hero=0;
	 int goal_enemy=0;

(2)在AnimationScene.cpp的init函数中,使用createWithBMFont函数创建Label的两个实例,分别用于显示第几回合和比分信息,回合数的值等于英雄赢的回合数goal_hero与敌人赢的回合数goal_enemy的和再加上1,用Value的asString函数将整数转化为字符串显示。

//背景文字round
	auto tips_round = Label::createWithBMFont("fonts/futura-48.fnt",
		"Round "+Value(goal_hero+ goal_enemy+1).asString());
	tips_round->setPosition(Vec2(visibleSize.width / 2, visibleSize.height - 50));
	this->addChild(tips_round, 5);

	// 背景文字VS和比分
	auto tips_vs = Label::createWithBMFont("fonts/futura-48.fnt",
		Value(goal_hero).asString()+"  VS  "+ Value(goal_enemy).asString());
	tips_vs->setPosition(Vec2(visibleSize.width/2,visibleSize.height - 120));
	this->addChild(tips_vs, 5);

(3)死亡动画播放需要时间,需要等待其播放结束才能进入下一回合,因此我添加了一个计时器,角色死亡3s后,才开始下一回合。先在AnimationScene.h中声明开始计时的标志is_countdown、时间countdown_time变量,以及计时函数countdown。

	//计时器
	bool is_countdown = false;
	void countdown(float dt);
	int countdown_time = 0;

(4)计时函数简单地让countdown_time自增1即可。

//计时
void AnimationScene::countdown(float dt)
{
	countdown_time++;
}

(5)定义nextRound函数,用于开始下一回合。在该函数中,先判断是否有角色死亡,如果有角色死亡,则根据is_countdown标志判断是否增加计时器,定义一个schedule,每一秒更新一次,目标函数countdown,这样countdown_time就会每秒增加1了。

    如果经过3秒,则调用英雄和敌人的isDeath函数判断哪方得分,然后关闭并重置计时,将其在下一回合能够重新使用,最后调用removeAllChildren函数将所有子节点都移除,并重新调用init函数初始化游戏界面,这样就进入了下一回合。

//进入下一回合
void AnimationScene::nextRound()
{
	if (m_player->isDeath()|| m_enemy->isDeath())
	{
		if (!is_countdown)
		{
			//计时器计时3s
			this->schedule(schedule_selector(AnimationScene::countdown), 1.0f);
			is_countdown = true;
		}
		if (countdown_time == 3)
		{
			//得分
			if(m_player->isDeath())
				goal_enemy++;
			else if (m_enemy->isDeath())
			{
				goal_hero++;
			}
			//重置计时器
			is_countdown = false;
			countdown_time = 0;
			this->unschedule(schedule_selector(AnimationScene::countdown));
			//移除所有结点,重新初始化
			this->removeAllChildren();
			this->init();
		}
	}
}

(6)尝试运行程序,上方区域能够显示当前为第几回合和比分的信息,当角色死亡后,死亡动画播放完毕才会进入下一回合。

 3. 设计英雄Defend动作动画

 (1)在cocostudio的Animation Editor中新建一个项目,然后将英雄各个部位的图片素材添加到Resources文件夹中。向画布中添加图片素材,为每个身体部位添加骨骼,并且将骨骼和对应的身体部位绑定,接着绑定骨骼之间的父子关系。

(2)在对象结构窗口中可以调整图层的覆盖关系,调整身体部位的图层并拼接好每一部分。

 

(3)切换到动画模式,在该项目中添加一个动画defend,在动画帧窗口中添加关键帧,设计防御动作。

(4)防御动作如下图所示,前5帧从loading状态到防御状态,防御状态保持25帧,最后5帧从防御状态恢复到loading状态。

 

(5)在最后一帧的关键帧中添加帧事件“defend_end”,这样在cocos2d-x中可以监听该事件,从而判断防御动画是否结束。

(6)导出动画,并将导出的文件添加到“侠客行”项目的Resources文件夹中。

4. 将动画添加到游戏中

(1)在Hero.cpp的init函数中,通过ArmatureManager的addArmatureFileInfo加载导出的动画的json文件,然后创建Armature的实例,这样动画就被封装到一个Armature对象中了。

播放的第一个动画是待机动画“loading”,接着调用了setFrameEventCallFunc函数添加帧动画监听器。

	ArmatureDataManager::getInstance()->addArmatureFileInfo("Chapter06/AnimationScene/animation/Hero/Hero.ExportJson");
	m_armature = Armature::create("Hero");
	if (m_armature == NULL)
	{
		CCLOG("hero load error!");
		return false;
	}
	m_armature->setPosition(Vec2::ZERO);
	m_armature->getAnimation()->play("loading");
	m_armature->getAnimation()->setFrameEventCallFunc(CC_CALLBACK_0(Hero::onFrameEvent, this,
		std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
	this->addChild(m_armature);
	this->setPosition(position);

(2)新创建的动画和以前的动画人物位置不一样,因此要重设动画在场景中的位置。

    // hero
	m_player = Hero::create(Vec2(visibleSize.width/4, 20));
	this->addChild(m_player, 3);

 (3)在config_set.h的变量State中添加一项值“DEFEND”,表示角色处于防御状态,接着在Hero.h中声明两个布尔型变量m_isdefend和m_isdefend_end,还定义了isDefend_end函数获取m_isdefend_end变量的值。

enum State
{
	STAND,
	MOVELEFT,
	MOVERIGHT,
	ATTACK,
	DEATH,
	SMITTEN,
	DEFEND
};
	bool m_isdefend;//防御
	bool m_isdefend_end;

	bool isDefend_end() { return m_isdefend_end; }

(4)在Hero类的init函数中添加一个执行防御动作的按钮,通过Create函数创建Button的实例,然后为其添加触摸事件监听器,当触摸事件结束时,调用Hero类的play函数,传入参数“DEFEND”,最后为按钮设置位置并添加到场景中。

//防御
	Button* Defendbutton = Button::create("Chapter06/AnimationScene/animation/control/d-btn.png", 
		"Chapter06/AnimationScene/animation/control/d-btn.png");
	Defendbutton->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type)
		{
			switch (type)
			{
			case cocos2d::ui::Widget::TouchEventType::BEGAN:
				break;
			case cocos2d::ui::Widget::TouchEventType::MOVED:
				break;
			case cocos2d::ui::Widget::TouchEventType::ENDED:
				m_player->play(DEFEND);
				break;
			}
		});
	Defendbutton->setScale(1.3);
	Defendbutton->setPosition(Vec2(visibleSize.width - 180, 100));
	this->addChild(Defendbutton,5);

(5)修改Hero类的play函数,当传入参数为“DEFEND”且m_isdefend_end为false时,才执行如下图所示的语句,变量m_isdefend值改为true,表示可以播放防御动画。

变量m_isdefend_end用于表示防御动画是否结束,因为上一次的防御动画进行时不能开始新的防御动画,m_isdefend_end值为false时表示防御动画播放完毕。

void Hero::play(State state)
{
	if (state == SMITTEN) // 控制被击中时颤抖动画只播放一次
	{
		m_ishurt = true; 
		
	}
	//上一次的攻击动画结束后才开始新的动画
	else if (state == ATTACK&& m_isAttack_end=&
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值