Harmony를 시작하기 전에 - solaris0115/RimWorldModGuide GitHub Wiki

Harmony 라이브러리란?

Harmony(이하 하모니)는 런타임 중 .Net 메서드를 접근, 제어 하기 위한 라이브러리입니다.
하모니는 다음과 같은 기능들을 제공합니다.

  • 기존의 코드를 유지한 채 아래의 기능들 수행
  • 해당 기능 전후에 추가 기능 실행
  • 기존 기능 수정[IL]
  • 하나의 개체에 하모니를 통한 복수의 접근, 제어시 충돌 방지.

사용 최소 조건

하모니는 .Net 2.0이상부터 작동 가능하도록 설계되어 있습니다. 림월드에 쓰인 Unity 버전은 `.Net 3.5'를 지원함으로 큰 문제가 없을 것입니다. 그외 다른 의존성은 없으며 비슷한 타게임의 개발 환경에서도 작동할 가능성이 있습니다.
하모니는 기본적으로 PC, Mac, Linux에서 테스트 되었으며 32/64 bit 모두 지원합니다.
일반적 Unity 개발 환경에서는 단순히 하모니 dll을 포함하시기만 하면 됩니다.

모든 버전에서 .Net Core에 대한 지원을 할 예정이며 현재 테스트 중 입니다.
프로젝트 내에 하모니dll을 추가하고 이후 ILMerge와 같은 도구로 어셈블리 출력물에 합치세요.
하모니 라이브러리의 이름은 0Harmony.dll입니다.

간단한 예제

게임 시작시 나오는 메인화면[행성]을 하모니를 통해 교체하는 예제입니다.

아래는 림월드 메인 배경을 그리는 기존의 코드입니다.

namespace RimWorld
{
	[StaticConstructorOnStartup]
	public class UI_BackgroundMain : UIMenuBackground
	{
		private static readonly Vector2 BGPlanetSize = new Vector2(2048f, 1280f);
		private static readonly Texture2D BGPlanet = ContentFinder<Texture2D>.Get("UI/HeroArt/BGPlanet", true);

		public override void BackgroundOnGUI()
		{
                        //원본배경의 크기를 잰뒤 현재 화면에 맞춰준다.
			bool flag = true;
			if ((float)UI.screenWidth > (float)UI.screenHeight * (UI_BackgroundMain.BGPlanetSize.x / UI_BackgroundMain.BGPlanetSize.y))
			{
				flag = false;
			}
			Rect position;
			if (flag)
			{
				float height = (float)UI.screenHeight;
				float num = (float)UI.screenHeight * (UI_BackgroundMain.BGPlanetSize.x / UI_BackgroundMain.BGPlanetSize.y);
				position = new Rect((float)(UI.screenWidth / 2) - num / 2f, 0f, num, height);
			}
			else
			{
				float width = (float)UI.screenWidth;
				float num2 = (float)UI.screenWidth * (UI_BackgroundMain.BGPlanetSize.y / UI_BackgroundMain.BGPlanetSize.x);
				position = new Rect(0f, (float)(UI.screenHeight / 2) - num2 / 2f, width, num2);
			}
                        //현재의 화면에 그림을 그려준다.(가로를 기준으로 세로비율을 맞춘다.)
			GUI.DrawTexture(position, UI_BackgroundMain.BGPlanet, ScaleMode.ScaleToFit);
		}
	}
}

아래는 하모니를 통해 사용자 지정 메인화면을 만드는 코드 입니다.

using Harmony;
using RimWorld;
using System.Reflection;
using UnityEngine;
using Verse;
using System;

namespace Solaris
{
    [StaticConstructorOnStartup]
    internal static class Solaris
    {
        static Solaris()
        {
            HarmonyInstance harmonyInstance = HarmonyInstance.Create("com.Solaris.rimworld.mod");
            harmonyInstance.PatchAll(Assembly.GetExecutingAssembly());
        }
    }
    [HarmonyPatch(typeof(UI_BackgroundMain)), HarmonyPatch("BackgroundOnGUI")]
    internal class BackGroundChanger
    {
        //본 모드의 사용자 지정 주소로 변경
        static readonly Texture2D CustomBackground = ContentFinder<Texture2D>.Get("UI/Icons/Background", true);
        internal static readonly Vector2 MainBackgroundSize = new Vector2(CustomBackground.width, CustomBackground.height);

        static bool Prefix()
        {
            if (CustomBackground != null)
            {
                float width = (float)UI.screenWidth;
                float num = (float)UI.screenWidth * (MainBackgroundSize.y / MainBackgroundSize.x);
                GUI.DrawTexture(new Rect(0f, (float)UI.screenHeight / 2f - num / 2f, width, num), CustomBackground, ScaleMode.ScaleToFit, true);
            }
            //false일경우 기존의 코드 미실행.
            //true면 실행[true일경우 prefix의 사용자 지정 배경이 그려진 뒤 기존의 그림이 그려짐으로 기존의 그림이 나옴.
            return false;
        }
    }
}

이후 해당 모드폴더에 Textures/UI/Icons/Background.png의 사용자 지정 메인화면을 넣으면 됩니다.

위 코드는 하모니의 Prefix를 이용해 만들어졌습니다. 기존의 코드인 UI_BackgroundMain.BackgroundOnGUI()가 실행되기 전
새로만든 코드가 먼저 동작하고 이후 기존의 코드는 동작하지 않는 방식입니다.
물론 이것외 Postfix, transpiler를 통해 동일한 기능을 만들 수 있습니다.

이로써 간단하게 하모니가 무엇을 하는지 알아 보았습니다.

하모니에 대한 자세한 내용은 다음 문서를 참고하세요.
다음: Bootstrapping

⚠️ **GitHub.com Fallback** ⚠️