Unity3D游戲-憤怒的小鳥游戲源碼和教程(一)


Unity憤怒的小鳥游戲教程


本文提供全流程,中文翻譯。

Chinar堅持將簡單的生活方式,帶給世人!

(擁有更好的閱讀體驗 —— 高分辨率用戶請根據需求調整網頁縮放比例)



AngryEva游戲效果:

這里寫圖片描述



1

Spring Joint 2D —— 彈簧關節



Spring Joint 2D : 是Unity提供的一個彈簧關節組件,可通過AddComponent添加

Unity會自動模擬彈簧的物理效果,來執行函數,使物體具備同樣的彈簧效果


注意:Spring Joint 2D 組件,需要指定鏈接一個的Rigidbody組件:

這個物體是所需固定位置的物體(且物體上必須有Rididbody組件)

在Spring Joint 2D組件下的 Connected Rigid Body 屬性中添加

舉個栗子黑白88

這里寫圖片描述
這里寫圖片描述


2

CameraFollow —— 相機跟隨,插值


Mathf.Clamp(posX, 0, 18)

數學函數.范圍(限定目標,0,到 18之間)

舉個栗子黑白88

/// <summary>
/// 相機在指定范圍跟隨
/// </summary>
private void CameraFollow()
{
    //記錄Eva的橫坐標
    float posX = transform.position.x;

    //相機當前位置 = 插值(當前相機位置,目標位置(Mathf.Clamp-限定范圍:(限定posX,018之間))
    Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, new Vector3(Mathf.Clamp(posX, 0, 18), Camera.main.transform.position.y, Camera.main.transform.position.z), SmoothFlo * Time.deltaTime);

}

這里寫圖片描述


3

RelativeVelocity —— 相對速度(- - 檢測受傷的方式)


collision.relativeVelocity.magnitude > MaxSpeed

碰撞物體的.相對速度.大小 > 最大速度

舉個栗子黑白88

/// <summary>
/// 觸發檢測,檢測是否達到受傷條件
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "Eva") //需在外部設置標簽,給Eva物體設置Tag為Eva
    {
        AudioPlay(EvaHurtClip);                         //播放受傷音效
        collision.transform.GetComponent<Eva>().Hurt(); //受傷
    }

    if (collision.relativeVelocity.magnitude > MaxSpeed) //如果相對速度.大小>最大速度
    {
        Dead(); //調用死亡消除方法
    }
    else if (collision.relativeVelocity.magnitude > MinSpeed && collision.relativeVelocity.magnitude < MaxSpeed) //相對速度在4-8之間
    {
        Render.sprite = HurtSprite; //更換圖片,受傷
        AudioPlay(HurtClip);
    }
}

4

Eva —— 腳本


舉個栗子黑白88

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;


/// <summary>
/// Eva類腳本
/// </summary>
public class Eva : MonoBehaviour
{
    public                   float          MaxDis    = 1.8f;  //Eva可拖動最遠距離
    public                   float          SmoothFlo = 3;     //平滑度
    [HideInInspector] public SpringJoint2D  EvaSP;             //彈簧鏈接組件
    protected                Rigidbody2D    EvaRg;             //剛體組件
    public                   LineRenderer   LeftLineRenderer;  //左線組件
    public                   Transform      LeftPos;           //彈弓左定點
    public                   LineRenderer   RightLineRenderer; //右線組件
    public                   Transform      RightPos;          //彈弓右定點
    protected                GameObject     EvaBoom;           //Eva爆炸特效
    protected                MyTrail        myTrail;           //定義拖尾腳本對象
    public                   AudioClip      SelectEvaClip;     //選中Eva音效
    public                   AudioClip      FlyEvaClip;        //Eva飛出音效
    protected                SpriteRenderer EvaRender;         //Eva渲染組件
    public                   Sprite         HurtSprite;        //受傷圖
    [HideInInspector] public bool           isCanTrail;        //是否能拖拽
    [HideInInspector] public bool           isRelease;         //是否釋放Eva
    private                  bool           isClick;           //是否點擊
    private                  bool           isFly;             //是否正在飛


    private void Awake()
    {
        EvaSP     = GetComponent<SpringJoint2D>(); //獲取組件
        EvaRg     = GetComponent<Rigidbody2D>();
        myTrail   = GetComponent<MyTrail>();
        EvaRender = GetComponent<SpriteRenderer>();
    }


    void Start()
    {
        EvaBoom = Resources.Load<GameObject>("Prefabs/EvaMumBoom");
    }


    // Update is called once per frame
    void Update()
    {
        if (EventSystem.current.IsPointerOverGameObject()) return; //如果點擊了UI界面上的按鈕,圖片。就不向下執行

        if (isClick)
        {
            transform.position =  Camera.main.ScreenToWorldPoint(Input.mousePosition);  //屏幕坐標轉世界
            transform.position += new Vector3(0, 0, -Camera.main.transform.position.z); //第二種方法:同理,加上攝像機的Z軸偏移量 --得正哦
            //transform.position += new Vector3(0,0,10);//第一種方法:既然攝像機在-10方向上,那么Eva就+10

            if (Vector3.Distance(transform.position, RightPos.position) > MaxDis) //如果大於設定距離MaxDis
            {
                Vector3 pos        = (transform.position - RightPos.position).normalized; //單位化向量,求得方向
                pos                *= MaxDis;                                             //賦值最大長度 給向量Pos
                transform.position =  pos + RightPos.position;                            //Eva當前位置賦值:最大距離+起點坐標點的位置
            }

            SlingShort();
        }


        CameraFollow(); //相機跟隨

        if (isFly) //如果在飛出的過程中
        {
            if (Input.GetMouseButtonDown(0)) //按下鼠標左鍵
            {
                EvaYellowExpedite(); //啟用黃Eva加速函數
            }
        }
    }


    /// <summary>
    /// 鼠標按下
    /// </summary>
    private void OnMouseDown()
    {
        if (isCanTrail)
        {
            AudioPlay(SelectEvaClip);
            isClick           = true; //點擊了
            EvaRg.isKinematic = true; //啟動力學
        }
    }


    /// <summary>
    /// 鼠標抬起
    /// </summary>
    private void OnMouseUp()
    {
        if (isCanTrail)
        {
            isClick                   = false; //沒點擊
            RightLineRenderer.enabled = false; //關閉右划線
            LeftLineRenderer.enabled  = false; //關閉左划線
            EvaRg.isKinematic         = false; //關閉力學
            Invoke("Fly", 0.1f);               //調用函數,(“函數名”,延遲時間)
            isCanTrail = false;
        }
    }


    /// <summary>
    /// 飛出后的處理
    /// </summary>
    private void Fly()
    {
        isRelease = true;      //鼠標抬起
        isFly     = true;      //正在飛,開始
        AudioPlay(FlyEvaClip); //播放音效
        EvaSP.enabled = false; //禁用彈簧鏈接
        Invoke("NextEva", 4);  //2秒后調用 下一個Eva函數
        myTrail.StartTrail();  //開啟拖尾
    }


    /// <summary>
    /// 彈弓
    /// </summary>
    private void SlingShort()
    {
        //給彈弓划線
        RightLineRenderer.enabled = true;
        LeftLineRenderer.enabled  = true;
        RightLineRenderer.SetPosition(0, RightPos.position);
        RightLineRenderer.SetPosition(1, transform.position);
        LeftLineRenderer.SetPosition(0, LeftPos.position);
        LeftLineRenderer.SetPosition(1, transform.position);
    }


    /// <summary>
    /// 下一只Eva
    /// </summary>
    protected virtual void NextEva()
    {
        GameManager.Instance.EvaList.Remove(this); //從Eva數組中移除當前Eva
        Destroy(gameObject);
        Instantiate(EvaBoom, transform.position, Quaternion.identity); //實例化特效
        GameManager.Instance.NextEva();                                //調用總控里的下一個判斷
    }


    /// <summary>
    /// 碰撞檢測
    /// </summary>
    /// <param name="collision"></param>
    private void OnCollisionEnter2D(Collision2D collision)
    {
        myTrail.ClearTrail(); //清除拖尾
        isFly          = false;
        Time.timeScale = 1;
    }


    /// <summary>
    /// 相機在指定范圍跟隨
    /// </summary>
    private void CameraFollow()
    {
        //記錄Eva的橫坐標
        float posX = transform.position.x;
        //相機當前位置 = 插值(當前相機位置,目標位置(Mathf.Clamp-限定范圍:(限定posX,0,18之間))
        Camera.main.transform.position = Vector3.Lerp(Camera.main.transform.position, new Vector3(Mathf.Clamp(posX, 0, 18), Camera.main.transform.position.y, Camera.main.transform.position.z), SmoothFlo * Time.deltaTime);
    }


    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="clip"></param>
    public void AudioPlay(AudioClip clip)
    {
        AudioSource.PlayClipAtPoint(clip, transform.position); //靜態方法:播放音效
    }


    /// <summary>
    /// 黃色Eva加速方法
    /// </summary>
    public virtual void EvaYellowExpedite()
    {
        isFly = false;
        AudioPlay(FlyEvaClip);
    }


    /// <summary>
    /// 受傷函數
    /// </summary>
    public void Hurt()
    {
        EvaRender.sprite = HurtSprite;
    }
}

這里寫圖片描述


5

EvaMum —— Eva媽媽腳本(- -敵人 )


提示:由於該游戲邏輯稍易,可被擊打對象為 EvaMum 與 場景中的可被拆除的建築物

所以此腳本可通用於:被擊打物體

至於是否容易被打死,打碎。取決於碰撞物的相對速度 MinSpeed 與 MaxSpeed 可自己設置

舉個栗子黑白88

using UnityEngine;


/// <summary>
/// EVA媽媽腳本
/// </summary>
public class EvaMum : MonoBehaviour
{
    public    float          MaxSpeed = 8; //默認最大速度
    public    float          MinSpeed = 3; //默認最小速度
    private   SpriteRenderer Render;       //圖片
    public    Sprite         HurtSprite;   //受傷圖片
    protected GameObject     Boom;         //爆炸特效
    public    GameObject     EvaMumScore;  //分數圖片
    public    bool           isEvaMum;     //是不是Eva媽媽
    public    AudioClip      EvaHurtClip;  //Eva受傷音效
    public    AudioClip      DeadClip;     //銷毀音效
    public    AudioClip      HurtClip;     //受傷音效


    private void Awake()
    {
        Render = GetComponent<SpriteRenderer>(); //獲取圖片渲染組件
    }


    // Use this for initialization
    void Start()
    {
        Boom = Resources.Load<GameObject>("Prefabs/EvaMumBoom");
    }


    /// <summary>
    /// 觸發檢測,檢測是否達到受傷條件
    /// </summary>
    /// <param name="collision"></param>
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Eva") //需在外部設置標簽,給Eva物體設置Tag為Eva
        {
            AudioPlay(EvaHurtClip);                         //播放受傷音效
            collision.transform.GetComponent<Eva>().Hurt(); //受傷
        }

        if (collision.relativeVelocity.magnitude > MaxSpeed) //如果相對速度.大小>最大速度
        {
            Dead(); //調用死亡消除方法
        }
        else if (collision.relativeVelocity.magnitude > MinSpeed && collision.relativeVelocity.magnitude < MaxSpeed) //相對速度在4-8之間
        {
            Render.sprite = HurtSprite; //更換圖片,受傷
            AudioPlay(HurtClip);
        }
    }


    /// <summary>
    /// 死亡消除
    /// </summary>
    public void Dead()
    {
        if (isEvaMum)
        {
            GameManager.Instance.EvaMumList.Remove(this); //移除一個EvaMum
        }
        Destroy(gameObject);                                                                                               //刪除EvaMum物體
        Instantiate(Boom, transform.position, Quaternion.identity);                                                        //實例化特效
        GameObject scoreobj = Instantiate(EvaMumScore, transform.position + new Vector3(0, 0.5f, 0), Quaternion.identity); //實例化分數
        Destroy(scoreobj, 1.5f);                                                                                           //刪除分數
        AudioPlay(DeadClip);                                                                                               //播放死亡音效
    }


    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="clip"></param>
    public void AudioPlay(AudioClip clip)
    {
        AudioSource.PlayClipAtPoint(clip, transform.position); //靜態方法:播放音效
    }
}

1

EvaYellow —— 黃色Eva腳本


黃色小鳥為:加速小鳥,速度乘以2

注意:由於其他特技類Eva,都屬於Eva。

所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()

這里另建一個 EvaYellow 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

舉個栗子黑白88

/// <summary>
/// 黃色Eva
/// </summary>
public class EvaYellow : Eva//繼承自父類Eva
{
    public override void EvaYellowExpedite()//重寫特技方法
    {
        base.EvaYellowExpedite();
        EvaRg.velocity *= 2; //速度2倍
    }
}

這里寫圖片描述


2

EvaBlack —— 黑色Eva腳本


黑色小鳥為:爆炸小鳥 —— 變大,且炸掉周邊敵人/物體

注意:由於其他特技類Eva,都屬於Eva。

所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()

這里另建一個 EvaBlack 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

舉個栗子黑白88

using System.Collections.Generic;
using UnityEngine;


/// <summary>
/// 黑Eva類腳本
/// </summary>
public class EvaBlack : Eva//繼承自父類Eva
{
    public List<EvaMum> EvaMumList = new List<EvaMum>(); //聲明一個敵人數組,用來存放EvaMum


    public override void EvaYellowExpedite() //重寫特技方法
    {
        base.EvaYellowExpedite();
        if (EvaMumList.Count > 0 && EvaMumList != null) //判空且有EvaMum存在
        {
            for (int i = 0; i < EvaMumList.Count; i++) //遍歷
            {
                EvaMumList[i].Dead(); //調用EvaMumList數組中的EvaMum物體的死亡方法
            }
        }
        ClearAction(); //調用爆炸動作函數
    }


    /// <summary>
    /// 碰撞檢測
    /// </summary>
    /// <param name="col"></param>
    private void OnTriggerEnter2D(Collider2D col)
    {
        if (col.tag == "Enemy")
        {
            EvaMumList.Add(col.GetComponent<EvaMum>()); //觸發器檢測到范圍內:有敵人,就加入敵人數組EvaMumList
        }
    }


    /// <summary>
    /// 退出檢測
    /// </summary>
    /// <param name="col"></param>
    private void OnTriggerExit2D(Collider2D col)
    {
        if (col.tag == "Enemy")
        {
            EvaMumList.Remove(col.GetComponent<EvaMum>()); //觸發器檢測到范圍內:無敵人,就移除敵人數組EvaMumList
        }
    }


    /// <summary>
    /// 處理爆炸動作
    /// </summary>
    private void ClearAction()
    {
        transform.localScale = new Vector3(5, 5, 0); //設置自身比例
        EvaRg.velocity       = Vector3.zero;         //速度歸零
        myTrail.ClearTrail();                        //清除軌跡
    }
}

這里寫圖片描述


6

GameManager ——游戲控制腳本


用來管理關卡場景中的游戲控制相關操作,掛載到空物體之上

注意:由於其他特技類Eva,都屬於Eva。

所以只需繼承自Eva,重寫Eva腳本中的特技方法 EvaYellowExpedite()

這里另建一個 EvaBlack 腳本,來重寫 Eva 中的 EvaYellowExpedite() 方法即可

舉個栗子黑白88

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;


/// <summary>
/// 游戲控制腳本
/// </summary>
public class GameManager : MonoBehaviour
{
    public static GameManager Instance
    {
        get { return instance; }

        set { instance = value; }
    }

    private static GameManager  instance;         //單例
    public         List<Eva>    EvaList;          //Eva數組
    public         List<EvaMum> EvaMumList;       //EvaMum數組
    private        Vector3      OriginPos;        //Eva初始化位置
    public         GameObject   WinPanel;         //勝利游戲面板
    public         GameObject   LosePanel;        //輸了游戲面板
    public         GameObject   PausePanel;       //輸了游戲面板
    public         GameObject[] Stars;            //星星數組
    private        Button       ListenButton;     //按鈕
    private        Animator     PauseAnimator;    //暫停動畫
    private        int          StarNum;          //星星數量
    private        bool         isPause;          //是否暫停
    public         int          StarLevelNum = 0; //開始關卡數
    public         int          EndLevelNum  = 3; //結束關卡數
    public         string       LevelCount;       //關卡標識符
    private        int          IndexCount = 0;   //記錄每關星星數量


    void Awake()
    {
        instance = this;
        if (EvaList.Count > 0)
        {
            OriginPos = EvaList[0].transform.position;
        } //如果存在Eva,記錄初始化位置
    }


    void Start()
    {
        Initialize(); //調用初始化函數
        StarNum = 0;
    }


    /// <summary>
    /// 初始化函數
    /// </summary>
    private void Initialize()
    {
        for (int i = 0; i < EvaList.Count; i++)
        {
            if (i == 0)
            {
                EvaList[i].transform.position = OriginPos; //給第一個小鳥初始化位置
                EvaList[i].enabled            = true;      //激活第一個Eva腳本
                EvaList[i].EvaSP.enabled      = true;      //激活第一個彈簧鏈接組件
                EvaList[i].isCanTrail         = true;
            }
            else
            {
                EvaList[i].enabled       = false; //關閉所有Eva腳本
                EvaList[i].EvaSP.enabled = false; //關閉所有彈簧鏈接組件
            }
        }
    }


    /// <summary>
    /// 判斷是否啟用下一個Eva
    /// </summary>
    public void NextEva()
    {
        if (EvaMumList.Count <= 0) //如果敵人依舊存在
        {
            WinPanel.SetActive(true); //勝利游戲面板
            AddButtonListen("WinRePlay");
            ListenButton.onClick.AddListener(RePlay);
            AddButtonListen("WinMainMenu");
            ListenButton.onClick.AddListener(Home);
        }
        else
        {
            if (EvaList.Count > 0) //如果Eva存在
            {
                Initialize(); //初始化Eva
            }
            else
            {
                LosePanel.SetActive(true); //結束游戲面板
                AddButtonListen("LoseRePlay");
                ListenButton.onClick.AddListener(RePlay);
                AddButtonListen("LoseMainMenu");
                ListenButton.onClick.AddListener(Home);
            }
        }
    }


    /// <summary>
    /// 贏了顯示星星
    /// </summary>
    public void WinAndShowStar()
    {
        StartCoroutine(ShowStars()); //開啟協成,一個個顯示
    }


    /// <summary>
    /// 一個個顯示星星協成
    /// </summary>
    /// <returns></returns>
    private IEnumerator ShowStars()
    {
        for (; StarNum < EvaList.Count + 1; StarNum++)
        {
            if (StarNum >= Stars.Length) break; //如果小鳥數量大於星星數量,就跳出:防止越界

            yield return new WaitForSeconds(0.5f);

            Stars[StarNum].SetActive(true); //開啟星星
        }
    }


    /// <summary>
    /// 添加按鈕事件
    /// </summary>
    private void AddButtonListen(string str)
    {
        ListenButton = GameObject.Find(str).GetComponent<Button>();
    }


    /// <summary>
    /// 重新開始
    /// </summary>
    public void RePlay()
    {
        if (isPause)
        {
            SceneManager.LoadScene(2);
            Time.timeScale = 1;
        }
        else
        {
            SceneManager.LoadScene(2);
            SaveData(); //儲存數據
        }
    }


    /// <summary>
    /// 回到主頁
    /// </summary>
    public void Home()
    {
        if (isPause)
        {
            SceneManager.LoadScene(1);
        }
        else
        {
            SceneManager.LoadScene(1);
            SaveData(); //儲存數據
            Time.timeScale = 1;
        }
    }


    /// <summary>
    /// 暫停游戲
    /// </summary>
    public void PauseGame()
    {
        PausePanel.SetActive(true);
        PauseAnimator = PausePanel.GetComponent<Animator>(); //獲取暫停動畫機
        PauseAnimator.SetBool("isPause", true);
        AddButtonListen("RePlayButton");
        ListenButton.onClick.AddListener(RePlay);
        AddButtonListen("HomeButton");
        ListenButton.onClick.AddListener(Home);
        AddButtonListen("ContinueButton");
        ListenButton.onClick.AddListener(PauseResume);
        isPause = true; //暫停游戲了

        if (GameManager.Instance.EvaList.Count > 0) //如果場景里還有Eva
        {
            if (GameManager.Instance.EvaList[0].isRelease == false) //如果沒有飛出
            {
                GameManager.Instance.EvaList[0].isCanTrail = false;
            }
        }
    }


    /// <summary>
    /// 繼續游戲
    /// </summary>
    public void PauseResume()
    {
        Time.timeScale = 1;
        PauseAnimator.SetBool("isPause", false);
        isPause = false; //關閉暫停

        if (GameManager.Instance.EvaList.Count > 0)
        {
            if (GameManager.Instance.EvaList[0].isRelease == false)
            {
                GameManager.Instance.EvaList[0].isCanTrail = true;
            }
        }
    }


    private int num          = 0;
    private int IndexCount10 = 0;
    private int IndexCount20 = 0;


    /// <summary>
    /// 存儲數據
    /// </summary>
    private void SaveData()
    {
        if (StarNum > PlayerPrefs.GetInt(PlayerPrefs.GetString("NowLevel"))) //判斷
        {
            PlayerPrefs.SetInt(PlayerPrefs.GetString("NowLevel"), StarNum); //分別設置每個關卡的星星個數
        }
        //所有星星數量相加


        if (LevelCount == "0")//通過標識符判斷是哪一大系列關卡,並對數據進行保存
        {
            for (int i = StarLevelNum; i <= EndLevelNum; i++)
            {
                num += PlayerPrefs.GetInt("Level (" + i + ")");
            }

            PlayerPrefs.SetInt("Level_1", num);
        }
        else if (LevelCount == "10")
        {
            for (int i = StarLevelNum; i <= EndLevelNum; i++)
            {
                num += PlayerPrefs.GetInt("Level (" + i + ")");
            }

            PlayerPrefs.SetInt("Level_2", num);
        }
        else if (LevelCount == "20")
        {
            for (int i = StarLevelNum; i <= EndLevelNum; i++)
            {
                num += PlayerPrefs.GetInt("Level (" + i + ")");
            }

            PlayerPrefs.SetInt("Level_3", num);
        }

        PlayerPrefs.SetInt("AllStarNum",
            PlayerPrefs.GetInt("Level_1") + PlayerPrefs.GetInt("Level_2") +
            PlayerPrefs.GetInt("Level_3")); //在“AllStarNum”中存儲總星星數量//將所有產生數據的關卡星星數量總和
    }
}

這里寫圖片描述


7

Next Tutorial —— 下一個教程



至此游戲場景相關結束,需要結合關卡場景

請跳轉至另一個教程 —— Unity3D游戲憤怒的小鳥游戲源碼和教程(二)


支持

May Be —— 搞開發,總有一天要做的事!


擁有自己的服務器,無需再找攻略!

Chinar 提供一站式教程,閉眼式創建!

為新手節省寶貴時間,避免采坑!


先點擊領取 —— 阿里全產品優惠卷 (享受最低優惠)


1 —— 雲服務器超全購買流程 (新手必備!)

2 —— 阿里ECS雲服務器自定義配置 - 購買教程(新手必備!)

3—— Windows 服務器配置、運行、建站一條龍 !

4 —— Linux 服務器配置、運行、建站一條龍 !





技術交流群:806091680 ! Chinar 歡迎你的加入


END

本博客為非營利性個人原創,除部分有明確署名的作品外,所刊登的所有作品的著作權均為本人所擁有,本人保留所有法定權利。違者必究

對於需要復制、轉載、鏈接和傳播博客文章或內容的,請及時和本博主進行聯系,留言,Email: ichinar@icloud.com

對於經本博主明確授權和許可使用文章及內容的,使用時請注明文章或內容出處並注明網址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM