Sofiane Kaci
LogoUnity Eplitz - PREMA STUDIO Eplitz Website
https://www.eplitz.com/


Type
Multiplayer
Genre
MOBA
Moteur
Unity
Langage
C#
Rôles
Gameplay Programmer
Gestion des serveurs
UI et shader
Intégration
Eplitz est un MOBA immersif où s'affrontent deux équipes de 4 joueurs dans une forêt maudite, pleine de mystères et de dangers cachés. Chaque recoin de cette forêt renferme des secrets anciens, des créatures redoutables, et des pouvoirs mystiques à découvrir pour prendre l'avantage sur l'équipe adverse. down-arrow Gameplay Programmation down-arrow Fog Of War Sur Eplitz, j'ai pu développer un custom fog of war multijoueur.

Etant un MOBA, jeu compétitif multijoueur, les calculs et informations concernant le fog of war sont entièrement effectués côté serveurs afin d'éviter toute désynchronisation entre les joueurs.

Il a aussi la particularité d'être différent selon l'équipe du joueur, une équipe ne doit pas avoir accès aux informations de l'autre équipe.

De plus, une large quantité d'entités (tourelle, personnage et mercenaire) peuvent alors révéler la vision à leur équipe simultanément, le fog of war doit alors être optimisé afin de rester fluide malgré le nombre d'entité présente dans les différentes équipes.

Le fog of war de chaque équipe est calculé individuellement, j'ai opté pour une approche optimisé et optimisable à l'aide de simples paramètres, sous forme de grille.

Le fog of war s'actualise tous les "tick", une durée prédéfinie, et non à chaque frame..


Etapes

FogOfWarDebug
Détection des zones visibles sur la grille

FogOfWarDebug2
Utilisation de shader pour transformer la grille en texture

FogOfWar_EnnemyInteraction
Intéraction avec les ennemies

FogOfWar_BushInteraction
Intéraction avec les bush et smooth appliqué sur le shader.


C# script

Voici un code snippet de mon FogOfWarManager.cs visant à détecter les zones visibles ou non, prenant en compte les obstacles et les bush.
private void UpdateVisibilityGrid(List team, bool[,] visibilityGrid)
{
    foreach (var teamMember in team)
    {
        Vector3 position = teamMember.transform.position;
        float range = teamMember.sightRange;

        // Calculate range around teamMember
        int startX = Mathf.Max(0, Mathf.FloorToInt((position.x - range - gridOrigin.x) / cellSize));
        int endX = Mathf.Min(gridWidth - 1, Mathf.FloorToInt((position.x + range - gridOrigin.x) / cellSize));
        int startZ = Mathf.Max(0, Mathf.FloorToInt((position.z - range - gridOrigin.z) / cellSize));
        int endZ = Mathf.Min(gridHeight - 1, Mathf.FloorToInt((position.z + range - gridOrigin.z) / cellSize));

        Collider[] obstaclesInRange = Physics.OverlapSphere(position, range, obstacles);
        Collider[] visibleBushes = Physics.OverlapSphere(position, 0.5f, LayerMask.GetMask("Bushes"));

        foreach (Collider visibleBush in visibleBushes)
        {
            obstaclesInRange = obstaclesInRange.Where(obstacle => obstacle != visibleBush).ToArray();
        }

        // Check visibility for cells within the range
        for (int x = startX; x <= endX; x++)
        {
            for (int z = startZ; z <= endZ; z++)
            {
                Vector3 cellPosition = GetCellWorldPosition(x, z);
                if (Vector3.Distance(position, cellPosition) <= range)
                {
                    if (!IsObstacleBetween(position, cellPosition, obstaclesInRange))
                    {
                        visibilityGrid[x, z] = true;
                    }
                }
            }
        }
    }
}



private bool IsObstacleBetween(Vector3 start, Vector3 end, Collider[] obstacles)
{
    Vector3 direction = (end - start).normalized;
    float distance = Vector3.Distance(start, end);

    foreach (var obstacle in obstacles)
    {
        float rayDistance;

        // Check if there is obstacle blocking the view
        if (obstacle.bounds.IntersectRay(new Ray(start, direction), out rayDistance))
        {
            if (distance > rayDistance)
            {
                return true;
            }
        }
    }
    return false;
}
                                
Intégration des personnages J'ai pu aider à l'intégration des personnages du projet, principalement afin d'intégrer et/ou fluidifier les différentes animations entre elles, à l'aide de behaviour scripts et de l'animator d'Unity. Tobu_R2 Tobu R2 - Divine Meteor Tobu_E2 Tobu E2 - Sky Flee Auto_Distance Auto Attaque - Distance Auto_Close Auto Attaque - Corps à corps
down-arrow UI down-arrow J'ai eu l'occasion de pouvoir travailler sur l'UI du joueur.

Chaque personnage possède des visuels qui lui sont propre.
En gagnant un niveau, le joueur peut décider de débloquer un nouveau talent, ou d'améliorer un talent existant afin de changer la manière dont celui-ci fonctionne. Ceux ci ne doivent être visible uniquement lorsque le joueur peut les améliorer.

Lorqu'il lance un talent, son cooldown est également affiché sur l'UI à l'aide de shader, afin d'améliorer les feedbacks du joueur.

La vie du joueur s'adapte également en temps réel, à chaque fois que la valeur de celle-ci est modifiée.

Les statistiques sur la gauche de l'UI permettent d'améliorer le personnage en augmentant différentes catégories comme la vitesse, vitesse d'attaque, dégâts, etc... Elle possède un sytème de point qui lui est propre, obtenu à chaque montée de niveau, comme pour les talents. PlayerUI
UI adaptative du joueur