READY, AIM, FIRE

Space Shooter is a 3D networked multiplayer game where players battle in spaceships to score points.

void AuthoritativeServer::Receive( Message const& msg, IPv4Address const& address )
{
    if( msg.header.isReliable )
    {
	PacketWriter writer;
	ReliableAckMsg ack = ReliableAckMsg::Default();
	ack.SetSequence(msg.GetSequence());
	writer.AddMessage(ack);

	Packet ackPacket = writer.EmitMessage();
	int ackPacketSize = ackPacket.header.size;
	m_endpoint->SendMessage( 
            m_netSys->GenerateNewMessage( m_bindAddress, address, 
            reinterpret_cast<char*>(&ackPacket), ackPacketSize ) 
        );
    }

    Message const* msgPtr = &msg;

    switch( msg.header.id )
    {
	case MessageId::RELIABLE_ACK: 
        {
            HandleMessage( reinterpret_cast<ReliableAckMsg const*>(msgPtr), address );
            break;
        }
	case MessageId::CLIENT_JOIN_REQUEST: 
        {
            HandleMessage( reinterpret_cast<ClientJoinRequestMsg const*>(msgPtr), address );
            break;
        }
	case MessageId::CLIENT_PLAYER_CREATED:
        {
            HandleMessage( reinterpret_cast<PlayerCreatedMsg const*>(msgPtr), address );
            break;
        }
	case MessageId::CLIENT_DISCONNECT: 
        {
            HandleMessage( reinterpret_cast<ClientDisconnectMsg const*>(msgPtr), address ); 
            break;
        }
	case MessageId::UPDATE_PLAYER: 
        {
            HandleMessage( reinterpret_cast<PlayerUpdateMsg const*>(msgPtr), address );
            break;
        }
	case MessageId::ENTITY_REQUEST:
        {
            HandleMessage( reinterpret_cast<EntityRequestMsg const*>(msgPtr), address ); 
            break;
        }
	default: break;
    }
}
// Pixel stage for lighting models
float4 PixelMain(v2p_t input) : SV_Target0
{   
    float3 light = float3(0.577, 0.577, 0.577);
    
    // construction of TBN matrix
    float3 n = input.normal;
    float3 b = input.binormal;
    float3 t = input.tangent;
    
    t = normalize(t);
    b = normalize(b - t * dot(t, b) / dot(t, t));
    n = normalize(n - t * dot(t, n) / dot(t, t) - b * dot(b, n) / dot(b, b));
    float3x3 tangentMat = float3x3(t, b, n);
    
    float2 texCoord = input.uv;
    float4 normalVal = NormalMap.Sample(SurfaceSampler, texCoord);
    float3 normalValToFloat = 2 * normalVal.xyz - 1;
    
    float3 normalValInWorld = normalize(mul(normalValToFloat, tangentMat));
    
    // lighting calc with transformed normal map normal
    float3 reflection = reflect(-normalize(light), normalValInWorld);
    float3 toEye = (CameraPosition - input.worldPosition).xyz;
    
    float3 diffuseIntensity = max(dot(normalValInWorld, normalize(light)), 0);
    float3 specularIntensity = pow(max(dot(normalize(reflection), normalize(toEye)), 0), SpecularExp);
    float3 ambientIntensity = max(dot(normalValInWorld, -normalize(light)), 0);
    
    // Taper off diffuse and specular
    if( dot( n, light ) <= 0 )
    {
        float t = (dot(n, light) + 0.3) / (0.3);
        t = clamp(t, 0, 1);
        
        diffuseIntensity = diffuseIntensity * t;
        specularIntensity = specularIntensity * t;
    }
    
    float4 textureColor = SurfaceTexture.Sample(SurfaceSampler, texCoord);
    
    float4 lightColor = DiffuseColor * float4(diffuseIntensity, 1) 
                      + SpecularColor * float4(specularIntensity, 1) 
                      + AmbientColor * float4(ambientIntensity, 1)
                      + float4(0.05, 0.05, 0.05, 1);
	
    return float4( input.color * textureColor * lightColor );
}
// Geometry stage for particle system
[maxvertexcount(6)]
void GeometryMain(point v2g_t input[1], inout TriangleStream<g2p_t> stream)
{
    g2p_t output[4];
    
    float4 vertex;
    for(int idx = 0; idx < 4; ++idx)
    {
        vertex = input[0].position + offset[idx] * float4(0.0, input[0].uv, 0.0);
        output[idx].position = mul(ProjectionMatrix, vertex);
        output[idx].color = input[0].color;
        output[idx].uv = uvCorner[idx];
    }

    stream.Append(output[0]);
    stream.Append(output[1]);
    stream.Append(output[3]);
    stream.RestartStrip();
    
    stream.Append(output[0]);
    stream.Append(output[3]);
    stream.Append(output[2]);
    stream.RestartStrip();
}

 Post-Mortem

In Space Shooter, my greatest challenge was learning new technology - mainly networking and lighting. The greatest hurdle in creating a networking framework was understanding how to integrate the server-client architecture into my standard project. There ended up being a lot of information traveling up and down from the server/client sockets to the game entities. Debugging issues with the netcode was also a rough task - however, that helped me learn how to thoroughly check every step of the pipeline.

Previous
Previous

SimpleMiner

Next
Next

Libra