← 返回项目列表

Doomenstein

为所有Actor绘制 3D Sprite,并根据视线方向和时间查找正确的动画帧

C++RaycastingData-DrivenMultiplayer

3D Sprite

Actor Sprites

Actor Sprites

3D Sprite

void Actor::Render( Camera const& camera ) const
{
	if (g_theRenderer != nullptr)
	{
		for (int i = 0; i < g_theGame->m_playerControllers.size(); i++)
		{
			if (m_currentController == g_theGame->m_playerControllers[i] && &g_theGame->m_playerControllers[i]->m_playerCamera == &camera)
			{
				return;
			}
		}	
		Vec3 const& cameraPosition = camera.GetPosition();
		Vec3 toActorVec = (m_position - cameraPosition);
		Vec3 toActorVec2D( toActorVec.x, toActorVec.y, 0.f );
		std::string currentStateName;
		if (m_actorState == ActorState::WALK)
		{
			currentStateName = "Walk";
		}
		else if (m_actorState == ActorState::ATTACK)
		{
			currentStateName = "Attack";
		}
		else if (m_actorState == ActorState::HURT)
		{
			currentStateName = "Hurt";
		}
		else if (m_actorState == ActorState::DEATH)
		{
			currentStateName = "Death";
		}
		else if (m_actorState == ActorState::IDLE)
		{
			currentStateName = "Walk";
		}
		toActorVec2D = m_orientation.GetAsMatrix_IFwd_JLeft_KUp().GetOrthonormalInverse().TransformVectorQuantity3D( toActorVec2D ).GetNormalized();
		for (int j = 0; j < m_actorDefinition->m_animationGroups.size(); j++)
		{
			if (currentStateName == m_actorDefinition->m_animationGroups[j].m_groupName)
			{
				int animDefIndex = 0;
				float maxDotValue = -99999.f;

				for (int k = 0; k < m_actorDefinition->m_animationGroups[j].m_animDefinitions.size(); k++)
				{
					Vec3 const& spriteDirection = m_actorDefinition->m_animationGroups[j].m_vectorDirections[k];
					float dotValue = DotProduct3D( spriteDirection, toActorVec2D );
					if (dotValue > maxDotValue)
					{
						maxDotValue = dotValue;
						animDefIndex = k;
					}
				}
				m_currentAnimDef = &m_actorDefinition->m_animationGroups[j].m_animDefinitions[animDefIndex];
				std::vector spriteVerts;
				std::vector spriteIndexes;
				spriteVerts.reserve( 4 );
				spriteIndexes.reserve( 6 );
				AABB2 spriteAABB;
				if (m_actorState != ActorState::IDLE)
				{
					if (m_actorDefinition->m_animationGroups[j].m_isScaleBySpeed)
					{
						float speedScaleRatio = m_velocity.GetLength() / m_actorDefinition->m_runSpeed;
						spriteAABB = m_currentAnimDef->GetSpriteDefAtTime( m_animationTimer * speedScaleRatio ).GetUVs();
					}
					else
					{
						spriteAABB = m_currentAnimDef->GetSpriteDefAtTime( m_animationTimer ).GetUVs();
					}
				}
				else
				{
					spriteAABB = m_currentAnimDef->GetSpriteDefAtTime( 0.f ).GetUVs();
				}

				Vec3 pointBL = Vec3( -m_actorDefinition->m_size.x * m_actorDefinition->m_pivot.x, -m_actorDefinition->m_size.y * m_actorDefinition->m_pivot.y, 0.f );
				Vec3 pointBR = Vec3( m_actorDefinition->m_size.x * (1.f - m_actorDefinition->m_pivot.x), -m_actorDefinition->m_size.y * m_actorDefinition->m_pivot.y, 0.f );
				Vec3 pointTR = Vec3( m_actorDefinition->m_size.x * (1.f - m_actorDefinition->m_pivot.x), m_actorDefinition->m_size.y * (1.f - m_actorDefinition->m_pivot.y), 0.f );
				Vec3 pointTL = Vec3( -m_actorDefinition->m_size.x * m_actorDefinition->m_pivot.x, m_actorDefinition->m_size.y * (1.f - m_actorDefinition->m_pivot.y), 0.f );
				
				//If Rounded render
				if (m_actorDefinition->m_isRenderRounded)
				{
					AddVertsForRoundedQuad3D( spriteVerts, spriteIndexes, pointBL, pointBR, pointTR, pointTL, Rgba8::WHITE, spriteAABB );
				}
				else
				{
					AddVertsForQuad3D( spriteVerts, spriteIndexes, pointBL, pointBR, pointTR, pointTL, Rgba8::WHITE, spriteAABB );
				}

				Mat44 cameraMat = camera.GetOrientation().GetAsMatrix_IFwd_JLeft_KUp();
				cameraMat.SetTranslation3D( camera.GetPosition() );

				Mat44 transformMat;
				transformMat = GetBillboardMatrix( m_actorDefinition->m_billboardType, cameraMat, m_position, Vec2( 1.f, 1.f ) );

				if (m_actorDefinition->m_shader!= nullptr)
				{
					g_theRenderer->BindShader( m_actorDefinition->m_shader );
				}
				g_theRenderer->SetRasterizerMode( RasterizerMode::SOLID_CULL_NONE );
				g_theRenderer->SetModelConstants( transformMat, m_color );
				g_theRenderer->BindTexture( &m_currentAnimDef->GetSpriteSheet().GetTexture() );
				g_theRenderer->DrawVertexArray( (int)spriteVerts.size(), spriteVerts.data(), (int)spriteIndexes.size(), spriteIndexes.data() );
				g_theRenderer->SetRasterizerMode( RasterizerMode::SOLID_CULL_BACK );
			}
		}
	}
}

给Actor绘制3D Sprite

XML数据驱动

所有武器和Actor的属性都从XML文件中读取。

WeaponDefinition::WeaponDefinition( XmlElement* weaponElement )
{
	m_weaponName = ParseXmlAttribute( *weaponElement, "name", m_weaponName );
	m_refireTime = ParseXmlAttribute( *weaponElement, "refireTime", m_refireTime );
	m_rayCount = ParseXmlAttribute( *weaponElement, "rayCount", m_rayCount );
	m_rayCone = ParseXmlAttribute( *weaponElement, "rayCone", m_rayCone );
	m_rayRange = ParseXmlAttribute( *weaponElement, "rayRange", m_rayRange );
	m_rayDamage = ParseXmlAttribute( *weaponElement, "rayDamage", m_rayDamage );
	m_rayImpulse = ParseXmlAttribute( *weaponElement, "rayImpulse", m_rayImpulse );
	m_projectileCount = ParseXmlAttribute( *weaponElement, "projectileCount", m_projectileCount );
	m_projectileActorName = ParseXmlAttribute( *weaponElement, "projectileActor", m_projectileActorName );
	m_projectileCone = ParseXmlAttribute( *weaponElement, "projectileCone", m_projectileCone );
	m_projectileSpeed = ParseXmlAttribute( *weaponElement, "projectileSpeed", m_projectileSpeed );
	m_meleeCount = ParseXmlAttribute( *weaponElement, "meleeCount", m_meleeCount );
	m_meleeArc = ParseXmlAttribute( *weaponElement, "meleeArc", m_meleeArc );
	m_meleeRange = ParseXmlAttribute( *weaponElement, "meleeRange", m_meleeRange );
	m_meleeDamage = ParseXmlAttribute( *weaponElement, "meleeDamage", m_meleeDamage );
	m_meleeImpulse = ParseXmlAttribute( *weaponElement, "meleeImpulse", m_meleeImpulse );
	m_isReload = ParseXmlAttribute( *weaponElement, "isReload", m_isReload );
	m_bulletCount = ParseXmlAttribute( *weaponElement, "bulletCount", m_bulletCount );
	m_reloadingTime = ParseXmlAttribute( *weaponElement, "reloadingTime", m_reloadingTime );
	m_canAiming = ParseXmlAttribute( *weaponElement, "canAiming", m_canAiming );
	m_recoilSpeed = ParseXmlAttribute( *weaponElement, "recoilSpeed", m_recoilSpeed );
	m_recoilTime = ParseXmlAttribute( *weaponElement, "recoilTime", m_recoilTime );
	XmlElement* hudElement = weaponElement->FirstChildElement( "HUD" );


	if (hudElement != nullptr)
	{
		std::string hudShader = ParseXmlAttribute( *hudElement, "shader", "None" );
		std::string baseTexture = ParseXmlAttribute( *hudElement, "baseTexture", "None" );
		std::string reticleTexture = ParseXmlAttribute( *hudElement, "reticleTexture", "None" );
		std::string aimingTexture = ParseXmlAttribute( *hudElement, "aimingTexture", "None" );
		m_hudShader = g_theRenderer->GetLoadedShaderByName( hudShader.c_str() );
		m_baseTexture = g_theRenderer->CreateTextureFromFile( baseTexture.c_str() );
		m_reticleTexture = g_theRenderer->CreateTextureFromFile( reticleTexture.c_str() );
		if (aimingTexture != "None")
		{
			m_aimingTexture = g_theRenderer->CreateTextureFromFile( aimingTexture.c_str() );
		}
		m_reticleSize = ParseXmlAttribute( *hudElement, "reticleSize", m_reticleSize );
		m_spriteSize = ParseXmlAttribute( *hudElement, "spriteSize", m_spriteSize );
		m_spritePivot = ParseXmlAttribute( *hudElement, "spritePivot", m_spritePivot );

		XmlElement* animationElement = hudElement->FirstChildElement( "Animation" );
		while (animationElement)
		{
			WeaponAnimation weaponAnim;
			weaponAnim.m_name = ParseXmlAttribute( *animationElement, "name", weaponAnim.m_name );
			weaponAnim.m_shader = ParseXmlAttribute( *animationElement, "shader", weaponAnim.m_shader );
			weaponAnim.m_spriteSheetName = ParseXmlAttribute( *animationElement, "spriteSheet", weaponAnim.m_spriteSheetName );
			weaponAnim.m_cellCount = ParseXmlAttribute( *animationElement, "cellCount", weaponAnim.m_cellCount );

			Texture* tex = g_theRenderer->CreateTextureFromFile( weaponAnim.m_spriteSheetName.c_str() );
			weaponAnim.m_spriteSheet = new SpriteSheet( *tex, weaponAnim.m_cellCount );

			int startFrame = ParseXmlAttribute( *animationElement, "startFrame", 0 );
			int endFrame = ParseXmlAttribute( *animationElement, "endFrame", 0 );
			float durationSeconds = ParseXmlAttribute( *animationElement, "secondsPerFrame", 0.f );
			int animNum = endFrame - startFrame + 1;
			SpriteAnimDefinition* animDefinition = new SpriteAnimDefinition( *weaponAnim.m_spriteSheet, startFrame, endFrame, durationSeconds * animNum );
			weaponAnim.m_animationDef = animDefinition;
			m_weaponAnimations.push_back( weaponAnim );
			animationElement = animationElement->NextSiblingElement();
		}
	}
	
	XmlElement* soundsElement = weaponElement->FirstChildElement( "Sounds" );
	if (soundsElement != nullptr)
	{
		XmlElement* soundElement = soundsElement->FirstChildElement( "Sound" );
		m_sound = ParseXmlAttribute( *soundElement, "sound", m_sound );
		m_soundName = ParseXmlAttribute( *soundElement, "name", m_soundName );
		m_soundID = g_audio->CreateOrGetSound( m_soundName, AudioFMode::AUDIO_FMOD_3D );
	}
}

解析 XML 文件

<Definitions>
    <!-- Pistol -->
    <WeaponDefinition name="Pistol" refireTime="0.75" rayCount="1" rayCone="0.0" rayRange="40.0" rayDamage="10.0~15.0" rayImpulse="4.0">
        <HUD shader="Default" baseTexture="Data/Images/Hud_Base.png" reticleTexture="Data/Images/Reticle.png" reticleSize="16,16" spriteSize="256,256" spritePivot="0.5,0.0">
            <Animation name="Idle" shader="Default" spriteSheet="Data/Images/Weapon_Pistol.png" cellCount="5,1" secondsPerFrame="0.1" startFrame="0" endFrame="0"/>
            <Animation name="Attack" shader="Default" spriteSheet="Data/Images/Weapon_Pistol.png" cellCount="5,1" secondsPerFrame="0.1" startFrame="1" endFrame="3" />
        </HUD>
        <Sounds>
            <Sound sound="Fire" name="Data/Audio/PistolFire.wav"/>
        </Sounds>
    </WeaponDefinition>

    <!-- Plasma Rifle -->
    <WeaponDefinition name="PlasmaRifle" refireTime="0.2" projectileCount="1" projectileActor="PlasmaProjectile" projectileCone="4.0" projectileSpeed="4.0">
        <HUD shader="Default" baseTexture="Data/Images/Hud_Base.png" reticleTexture="Data/Images/Reticle.png" reticleSize="16,16" spriteSize="256,256" spritePivot="0.5,0.0">
            <Animation name="Idle" shader="Default" spriteSheet="Data/Images/Weapon_Plasma.png" cellCount="4,1" secondsPerFrame="0.125" startFrame="0" endFrame="0" />
            <Animation name="Attack" shader="Default" spriteSheet="Data/Images/Weapon_Plasma.png" cellCount="4,1" secondsPerFrame="0.125" startFrame="1" endFrame="2" />
        </HUD>
        <Sounds>
            <Sound sound="Fire" name="Data/Audio/PlasmaFire.wav"/>
        </Sounds>
    </WeaponDefinition>

    <!-- Demon Melee -->
    <WeaponDefinition name="DemonMelee" refireTime="2.0" meleeCount="1" meleeArc="180.0" meleeRange="0.75" meleeDamage="2.0~4.0" meleeImpulse="2.0">
        <Sounds>
            <Sound sound="Fire" name="Data/Audio/DemonAttack.wav"/>
        </Sounds>
    </WeaponDefinition>

    <!-- SubMachine Gun -->
    <WeaponDefinition name="SubMachine" refireTime="0.1" rayCount="1" rayCone="0.0" rayRange="40.0" rayDamage="5.0~10.0" rayImpulse="4.0" isReload ="true" bulletCount ="20" reloadingTime ="2.0">
        <HUD shader="Default" baseTexture="Data/Images/Hud_Base.png" reticleTexture="Data/Images/Reticle.png" reticleSize="16,16" spriteSize="256,256" spritePivot="0.5,0.0">
            <Animation name="Idle" shader="Default" spriteSheet="Data/Images/WeaponShoot.png" cellCount="6,1" secondsPerFrame="0.1" startFrame="0" endFrame="0"/>
            <Animation name="Attack" shader="Default" spriteSheet="Data/Images/WeaponShoot.png" cellCount="6,1" secondsPerFrame="0.1" startFrame="1" endFrame="5" />
            <Animation name="Reload" shader="Default" spriteSheet="Data/Images/WeaponReload.png" cellCount="5,1" secondsPerFrame="0.4" startFrame="0" endFrame="4" />
        </HUD>
        <Sounds>
            <Sound sound="Fire" name="Data/Audio/PistolFire.wav"/>
        </Sounds>
    </WeaponDefinition>

    <!-- Sword -->
    <WeaponDefinition name="Sword" refireTime="0.8" meleeCount="1" meleeArc="180.0" meleeRange="1.2" meleeDamage="50.0~60.0" meleeImpulse="2.0">
        <HUD shader="Default" baseTexture="Data/Images/Hud_Base.png" reticleTexture="Data/Images/Reticle.png" reticleSize="16,16" spriteSize="600,400" spritePivot="0.6,0.0">
            <Animation name="Idle" shader="Default" spriteSheet="Data/Images/Sword.png" cellCount="2,2" secondsPerFrame="0.2" startFrame="0" endFrame="0"/>
            <Animation name="Attack" shader="Default" spriteSheet="Data/Images/Sword.png" cellCount="2,2" secondsPerFrame="0.2" startFrame="1" endFrame="3" />
        </HUD>
        <Sounds>
            <Sound sound="Fire" name="Data/Audio/Sword.mp3"/>
        </Sounds>
    </WeaponDefinition>
</Definitions>

武器的XML

碰撞

与地图碰撞

与演员发生碰撞

void Map::CollideActorWithMap( Actor* actor )
{
	if (actor != nullptr && !actor->m_isDead)
	{
		Vec2 actorPos = Vec2( actor->GetActorPosition().x, actor->GetActorPosition().y );
		if (actor->m_actorDefinition->m_collidesWithWorld)
		{
			for (int i = 0; i < (int)m_tiles.size(); i++)
			{
				if (DoDiscOverlapFixedAABB2D( actorPos, actor->GetActorPhysicsRadius(), m_tiles[i].GetBounds() ))
				{
					if (!actor->m_actorDefinition->m_dieOnCollide)
					{
						actor->m_position.z = GetClamped( actor->m_position.z, 0.f, 10.f - actor->m_actorDefinition->m_physicsHeight );
					}
					else
					{
						if (actor->m_position.z >= 10.f - actor->m_actorDefinition->m_physicsHeight || actor->m_position.z <= 0.f)
						{
							actor->m_isDead = true;
						}
					}
					if (m_tiles[i].m_tileDefinition->m_isTrap)
					{
						m_tiles[i].m_damageTimer += g_theApp->m_gameClock->GetDeltaSeconds();
						if (m_tiles[i].m_damageTimer >= m_tiles[i].m_tileDefinition->m_damageTime)
						{
							m_tiles[i].m_damageTimer = 0.f;
							actor->m_currentHealth -= m_tiles[i].m_tileDefinition->m_damage;
							actor->AddImpulse( m_tiles[i].m_tileDefinition->m_damageImpulse * Vec3(0.f,0.f,1.f) );
						}
					}
				}
			}
			IntVec2 tileCoords = GetTileCoordsForWorldPos( actorPos );
			PushActorOutOfWall( actor, tileCoords + IntVec2( 1, 0 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( 0, 1 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( -1, 0 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( 0, -1 ) );

			PushActorOutOfWall( actor, tileCoords + IntVec2( 1, 1 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( -1, 1 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( -1, -1 ) );
			PushActorOutOfWall( actor, tileCoords + IntVec2( 1, -1 ) );
		}
	}

}

与地图碰撞的代码

void Map::CollideBetweenTwoActors( Actor* actorA, Actor* actorB )
{
	if (actorA != nullptr && actorB != nullptr)
	{
		if (actorA->m_actorDefinition->m_name != "SpawnPoint" && actorB->m_actorDefinition->m_name != "SpawnPoint")
		{
			if (!actorA->m_actorDefinition->m_isProjectile && !actorB->m_actorDefinition->m_isProjectile)
			{
				if (actorA->m_actorDefinition->m_collidesWithActors && actorB->m_actorDefinition->m_collidesWithActors)
				{
					Vec2 actorAPos = Vec2( actorA->m_position.x, actorA->m_position.y );
					Vec2 actorBPos = Vec2( actorB->m_position.x, actorB->m_position.y );
					FloatRange actorARangeZ( actorA->m_position.z, actorA->m_position.z + actorA->m_actorDefinition->m_physicsHeight );
					FloatRange actorBRangeZ( actorB->m_position.z, actorB->m_position.z + actorB->m_actorDefinition->m_physicsHeight );
					if (actorARangeZ.DoFloatRangeOverlapEachOther( actorBRangeZ ))
					{
						PushDiscsOutOfEachOther2D( actorAPos, actorA->m_actorDefinition->m_physicsRadius, actorBPos, actorB->m_actorDefinition->m_physicsRadius );
						actorA->m_position.x = actorAPos.x;
						actorA->m_position.y = actorAPos.y;
						actorB->m_position.x = actorBPos.x;
						actorB->m_position.y = actorBPos.y;
					}
				}
				else if (actorA->m_actorDefinition->m_collidesWithActors && !actorB->m_actorDefinition->m_collidesWithActors)
				{
					Vec2 actorAPos = Vec2( actorA->m_position.x, actorA->m_position.y );
					Vec2 actorBPos = Vec2( actorB->m_position.x, actorB->m_position.y );
					FloatRange actorARangeZ( actorA->m_position.z, actorA->m_position.z + actorA->m_actorDefinition->m_physicsHeight );
					FloatRange actorBRangeZ( actorB->m_position.z, actorB->m_position.z + actorB->m_actorDefinition->m_physicsHeight );
					if (actorARangeZ.DoFloatRangeOverlapEachOther( actorBRangeZ ))
					{
						PushDiscOutOfFixedDisc2D( actorAPos, actorA->m_actorDefinition->m_physicsRadius, actorBPos, actorB->m_actorDefinition->m_physicsRadius );
						actorA->m_position.x = actorAPos.x;
						actorA->m_position.y = actorAPos.y;
					}
				}
				else if (!actorA->m_actorDefinition->m_collidesWithActors && actorB->m_actorDefinition->m_collidesWithActors)
				{
					Vec2 actorAPos = Vec2( actorA->m_position.x, actorA->m_position.y );
					Vec2 actorBPos = Vec2( actorB->m_position.x, actorB->m_position.y );
					FloatRange actorARangeZ( actorA->m_position.z, actorA->m_position.z + actorA->m_actorDefinition->m_physicsHeight );
					FloatRange actorBRangeZ( actorB->m_position.z, actorB->m_position.z + actorB->m_actorDefinition->m_physicsHeight );
					if (actorARangeZ.DoFloatRangeOverlapEachOther( actorBRangeZ ))
					{
						PushDiscOutOfFixedDisc2D( actorBPos, actorB->m_actorDefinition->m_physicsRadius, actorAPos, actorA->m_actorDefinition->m_physicsRadius );
						actorB->m_position.x = actorBPos.x;
						actorB->m_position.y = actorBPos.y;
					}
				}
			}
			else if (actorA->m_actorDefinition->m_isProjectile && !actorB->m_actorDefinition->m_isProjectile && actorA->m_owner != actorB)
			{
				Vec2 actorAPos = Vec2( actorA->m_position.x, actorA->m_position.y );
				Vec2 actorBPos = Vec2( actorB->m_position.x, actorB->m_position.y );
				FloatRange actorARangeZ( actorA->m_position.z, actorA->m_position.z + actorA->m_actorDefinition->m_physicsHeight );
				FloatRange actorBRangeZ( actorB->m_position.z, actorB->m_position.z + actorB->m_actorDefinition->m_physicsHeight );
				if (actorARangeZ.DoFloatRangeOverlapEachOther( actorBRangeZ ))
				{
					if (DoDiscsOverlap( actorAPos, actorA->m_actorDefinition->m_physicsRadius, actorBPos, actorB->m_actorDefinition->m_physicsRadius ))
					{
						if (actorA->m_actorDefinition->m_dieOnCollide)
						{
							if (!actorB->m_isInvincible)
							{
								actorB->m_currentHealth -= rng->RollRandomFloatInRange( actorA->m_actorDefinition->m_damageOnCollide.m_min, actorA->m_actorDefinition->m_damageOnCollide.m_max );
							}
							Vec3 impuseDirection( -actorA->m_velocity.GetNormalized() );
							actorB->AddImpulse( actorA->m_ownerWeapon->m_weaponDef->m_rayImpulse * impuseDirection );
							if (actorB->m_AIController != nullptr)
							{
								actorB->m_AIController->m_targetActor = actorA->m_owner;
								actorB->m_AIController->m_isHitByOtherActors = true;
							}
							if (actorA->m_actorDefinition->m_canFrozenTarget)
							{
								actorB->m_isFrozen = true;
								actorB->m_frozenTime = actorA->m_actorDefinition->m_frozenTime;
							}
							actorA->m_isDead = true;							
							if (g_theGame->m_playerControllers.size() > 1 && actorB->m_actorDefinition->m_name == "Marine" && actorB->m_currentHealth <= 0.f)
							{
								for (int i = 0; i < g_theGame->m_playerControllers.size(); i++)
								{
									if (g_theGame->m_playerControllers[i] != nullptr)
									{
										if (g_theGame->m_playerControllers[i]->GetActor() != actorB)
										{
											g_theGame->m_playerControllers[i]->m_killNum++;
										}
									}
								}
							}
						}
					}
				}
			}
			else if (!actorA->m_actorDefinition->m_isProjectile && actorB->m_actorDefinition->m_isProjectile && actorB->m_owner != actorA)
			{
				Vec2 actorAPos = Vec2( actorA->m_position.x, actorA->m_position.y );
				Vec2 actorBPos = Vec2( actorB->m_position.x, actorB->m_position.y );
				FloatRange actorARangeZ( actorA->m_position.z, actorA->m_position.z + actorA->m_actorDefinition->m_physicsHeight );
				FloatRange actorBRangeZ( actorB->m_position.z, actorB->m_position.z + actorB->m_actorDefinition->m_physicsHeight );
				if (actorARangeZ.DoFloatRangeOverlapEachOther( actorBRangeZ ))
				{
					if (DoDiscsOverlap( actorAPos, actorA->m_actorDefinition->m_physicsRadius, actorBPos, actorB->m_actorDefinition->m_physicsRadius ))
					{
						if (actorB->m_actorDefinition->m_dieOnCollide)
						{
							if (!actorA->m_isInvincible)
							{
								actorA->m_currentHealth -= rng->RollRandomFloatInRange( actorB->m_actorDefinition->m_damageOnCollide.m_min, actorB->m_actorDefinition->m_damageOnCollide.m_max );
							}
							Vec3 impuseDirection( actorB->m_velocity.GetNormalized() );
							actorA->AddImpulse( actorB->m_actorDefinition->m_impulseOnCollide * impuseDirection );
							if (actorA->m_AIController != nullptr)
							{
								actorA->m_AIController->m_targetActor = actorB->m_owner;
								actorA->m_AIController->m_isHitByOtherActors = true;
							}
							if (actorB->m_actorDefinition->m_canFrozenTarget)
							{
								actorA->m_isFrozen = true;
								actorA->m_frozenTime = actorB->m_actorDefinition->m_frozenTime;
							}
							actorB->m_isDead = true;
							if (actorA->m_actorDefinition->m_name == "Marine" && actorA->m_currentHealth <= 0.f)
							{
								for (int i = 0; i < g_theGame->m_playerControllers.size(); i++)
								{
									if (g_theGame->m_playerControllers[i] != nullptr)
									{
										if (g_theGame->m_playerControllers[i]->GetActor() != actorA)
										{
											g_theGame->m_playerControllers[i]->m_killNum++;
										}								 
									}
								}
							}
						}
					}
				}
			}
		}

	}

}

与 Actor 碰撞的代码

截图