하늘에 보내는 편지(Letter To Sky) | DirectX11 / 팀 프로젝트

    • 장르   |   3D 퍼즐
    • 언어   |   C / C++
    • 엔진   |   DirectX11 자체엔진(+FMOD)
    • 기간   |   21. 08. 02 ~ 21. 08. 27 (4주)
    • 인원   |   기획 1명, 아트 2명, 프로그래머 1명(본인)
    • 맡은 역할   |   엔진, 클라이언트 메인 프로그래머
    • 설명   |   하늘에 있는 분을 그리워하는 마음이 간절하여 만들어진 편지를 천사우체부가 배달하는 게임입니다.
    • GitHub   |   https://github.com/hajineys/Letter-To-Sky
     

    GitHub - hajineys/Letter-To-Sky: DirectX 11 자체엔진으로 만든 게임

    DirectX 11 자체엔진으로 만든 게임. Contribute to hajineys/Letter-To-Sky development by creating an account on GitHub.

    github.com


    1. 게임 구조

    2. 그래픽스 엔진 구조

    3. 플레이어 클래스

    캐릭터에 대한 다양한 상태 정의 클래스가 존재합니다.

    캐릭터의 상태에 따라 다르게 이동하고, 행동하며, 게임 진행을 판단합니다.

    플랫포머를 구현하기 위한 핵심적인 기능으로

    /// <summary>
    /// 전체 계단만 돌면서
    /// x/z 위치를 가지고서 충돌감지
    /// 
    /// 결과적으로 캐릭터가 '딛고' 서야 할 위치를 알려준다.
    /// 
    /// 충돌감지 위치가 없으면 없다고 리턴
    /// </summary>
    /// <param name="position">결과적으로 받아 갈 위치 (y값이 중요)</param>
    /// <param name="x">캐릭터의 x좌표값</param>
    /// <param name="z">캐릭터의 z좌표값</param>
    /// <returns></returns>
    bool TutorialInGameScene::GetCollisionPos(Vector3* position)
    {
    	DirectX::BoundingBox _boundingBox;
    	for (UINT i = 0; i < m_pStairList.size(); i++)
    	{
    		_boundingBox = m_pStairList[i]->GetStairObject()->GetBoxCollider()->GetBBForCollisionDetection();
    
    		Vector4 _origin(position->x, 1000.0f, position->z, 1.0f);
    		Vector4 _direction(0, -1.0f, 0, 1.0f);
    		float _resultDist = 1000.0f;
    
    		// 직선과 충돌을 했다면, 그 위치를 리턴한다.
    		if (_boundingBox.Intersects(_origin, _direction, _resultDist) == true)
    		{
    			//position->y = _origin.y + _resultDist;
    			if (m_pStairList[i]->GetType() == eStairType::Start_Base)
    			{
    				position->y = m_pStairList[i]->GetStairObject()->GetPosition().y + Y_CHARACTER_OFFSET_STARTBASE;
    			}
    			else if (m_pStairList[i]->GetType() == eStairType::Cloud_Base)
    			{
    				position->y = m_pStairList[i]->GetStairObject()->GetPosition().y + Y_CHARACTER_OFFSET_CLOUDBASE;
    			}
    			else if (m_pStairList[i]->GetType() == eStairType::End_Base)
    			{
    				position->y = m_pStairList[i]->GetStairObject()->GetPosition().y + Y_CHARACTER_OFFSET_ENDBASE;
    			}
    			else if (m_pStairList[i]->GetType() == eStairType::Cloud)
    			{
    				position->y = m_pStairList[i]->GetStairObject()->GetPosition().y + Y_CHARACTER_OFFSET_CLOUD;
    			}
    			else
    			{
    				position->y = m_pStairList[i]->GetStairObject()->GetPosition().y + Y_CHARACTER_OFFSET_STAIR;
    			}
    
    			return true;
    		}
    	}
    
    	// 충돌을 안했다는 것은, 허공에 떠 있다는 것
    	return false;
    }

    캐릭터의 위치에서 밑으로 광선을 쏘아 특정 계단의 위치 값을 가져오고

    바닥과 충돌했는지 하지 않았는지 체크하여 바닥의 위치로 캐릭터를 이동시킨다.

    또한 캐릭터가 밟은 계단의 정보값을 가져온다.

    (계단의 상태에 따라 캐릭터를 변화시켜줘야하기 때문이다.)

     

    4. 충돌 체크

    DirectXCollision.h에 있는 BoundingBox 구조체를 사용하여 BoxCollider 클래스를 만들었습니다.

    DXObject들간의 충돌체크를 위해 Object와 같이 이 클래스도 Transform을 상속받았습니다.

    충돌을 체크할 BoxCollider를 매개변수로 받아 나의 범위와 상대방의 범위가 교차하는지 여부를 확인하는

    Intersects 함수로 bool값을 return합니다.

     

    5. 2D Image

    이미지를 맨 상단에 그리기 위해 Z버퍼를 끕니다.

    2D를 위한 새로운 깊이 스텐실 상태 변수를 만듭니다.

    2D Image를 그리기 위해 직교 투영 행렬을 만듭니다.

    //=============================================================================
    // Bitmap.fx by Hakgeum
    //
    // Effect used to shade Bitmap
    //=============================================================================
    
    cbuffer cbPerFrame
    {
        float4x4 gWorldViewProj;
    };
    
    Texture2D shaderTexture : register(t0);
    SamplerState SampleType : register(s0);
    
    BlendState AlphaBlend
    {
        // 테두리 처리 False일 때
        AlphaToCoverageEnable = false;
    
        // 투명처리
        BlendEnable[0] = true; // 블렌드 모드 사용, 8개의 이미지까지 사용 가능
    
        /*
        블렌딩 함수
    
        INV_SRC_ALPHA : 소스 텍스쳐의 알파를 역전시킨 것
        즉, 1에서 소스 텍스쳐의 알파만큼 뺀 것.
    
        ex) 만약 소스의 알파가 0.3이라면 목표의 알파는 0.7로 보고 목표 픽셀의 70%를 사용
        */
    
        DestBlend[0] = INV_SRC_ALPHA; // 이미 그 자리에 그려져 있던 목표 픽셀의 색상
        SrcBlend[0] = SRC_ALPHA; // 소스 텍스쳐의 색상을 계산하는데 사용, (블렌딩 함수)SRC_ALPHA : 텍스쳐가 갖고있는 알파값 그대로사용
        BlendOp[0] = Add; // 연산 방법, Add : 더해서 최종 픽셀 구함
    
        SrcBlendAlpha[0] = One; // 한 개 쓰겠다.
        DestBlendAlpha[0] = One; // 한 개 쓰겠다.
        RenderTargetWriteMask[0] = 0x0f;
    };
    
    struct VertexIn
    {
        float3 PosL : POSITION;
        float2 Tex  : TEXCOORD0;
    };
    
    struct VertexOut
    {
        float4 PosH : SV_POSITION;
        float2 Tex  : TEXCOORD0;
    };
    
    VertexOut VS(VertexIn vin)
    {
        VertexOut vout = (VertexOut) 0;
        
        // Set z = w so that z/w = 1 (i.e., skydome always on far plane).
        vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj).xyww;
        
        vout.Tex = vin.Tex;
        
        return vout;
    }
    
    float4 PS(VertexOut pin) : SV_Target
    {
        float4 textureColor;
        
        textureColor = shaderTexture.Sample(SampleType, pin.Tex);
        
        return textureColor;
    }
    
    technique11 LightTech
    {
        pass P0
        {
            SetVertexShader(CompileShader(vs_5_0, VS()));
            SetPixelShader(CompileShader(ps_5_0, PS()));
    
            // 투명 처리 (텍스쳐 외의 뒷 배경색 제거)
            SetBlendState(AlphaBlend, float4(0, 0, 0, 0), 0xFF); // 0xFFFFFFFF = 0xFF 알아서 개수 맞춰넣어줌
        }
    }

    2D Image를 렌더하기 위한 Bitmap.fx

    UI로 사용할 2D Image를 3D Object와 동일하게 DX Factory를 통해 생성하고 관리합니다.

     

    6. CubeMap

    게임의 배경을 표현하기 위해 CubeMap을 사용하였다.

    //=============================================================================
    // Sky.fx by Frank Luna (C) 2011 All Rights Reserved.
    //
    // Effect used to shade sky dome.
    //=============================================================================
    
    cbuffer cbPerFrame
    {
    	float4x4 gWorldViewProj;
    };
     
    // Nonnumeric values cannot be added to a cbuffer.
    TextureCube gCubeMap;
    
    SamplerState samTriLinearSam
    {
    	Filter = MIN_MAG_MIP_LINEAR;
    	AddressU = Wrap;
    	AddressV = Wrap;
    };
    
    struct VertexIn
    {
    	float3 PosL : POSITION;
    };
    
    struct VertexOut
    {
    	float4 PosH : SV_POSITION;
        float3 PosL : POSITION;
    };
     
    VertexOut VS(VertexIn vin)
    {
    	VertexOut vout;
    	
    	// Set z = w so that z/w = 1 (i.e., skydome always on far plane).
    	vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj).xyww;
    	
    	// Use local vertex position as cubemap lookup vector.
    	vout.PosL = vin.PosL;
    	
    	return vout;
    }
    
    float4 PS(VertexOut pin) : SV_Target
    {
    	return gCubeMap.Sample(samTriLinearSam, pin.PosL);
    }
    
    RasterizerState NoCull
    {
        CullMode = None;
    };
    
    DepthStencilState LessEqualDSS
    {
    	// Make sure the depth function is LESS_EQUAL and not just LESS.  
    	// Otherwise, the normalized depth values at z = 1 (NDC) will 
    	// fail the depth test if the depth buffer was cleared to 1.
        DepthFunc = LESS_EQUAL;
    };
    
    technique11 SkyTech
    {
        pass P0
        {
            SetVertexShader( CompileShader( vs_5_0, VS() ) );
            SetGeometryShader( NULL );
            SetPixelShader( CompileShader( ps_5_0, PS() ) );
            
            SetRasterizerState(NoCull);
            SetDepthStencilState(LessEqualDSS, 0);
        }
    }

    댓글