해당 강의의 개발환경은 Forge : 1.7.10-10.13.0.1180, IDE : Eclipse입니다.

블록, 아이템 추가와 기본적인 모드 제작 능력이 있다고 가정하고 쓰는 글 입니다. 난이도는 중급 이상이므로 이 점 고려하시고 읽어주시길 바랍니다.

 

이 강좌에서 소개되는 방법은 코드를 이용하여 모델링을 하는 것을 주로 하고 있으며, 외부 프로그램에서 직접적으로 들여와서 게임에 적용시키는 것이 아니라는 것을 명심해주시길 바랍니다.

 

저번 강의에서 엔티티를 추가하기 위해서는 3가지의 클래스가 필요하다고 했습니다. 그 각각은 :

1) 엔티티 클래스 : 실질적인 엔티티의 행동양식을 담고 있다. -[Server]

2) 모델 클래스: 엔티티의 3D 정보를 담고 있다. -[Client]

3) 렌더 클래스: 엔티티의 겉 표면 그래픽을 담고있다. -[Client]

 

오늘은 이 중 2)과 3)을 직접 만들어 보겠습니다.

 

 

1.모델 클래스 만들기

 

1) 클래스 기본 형태 갖추기

모델을 만들기 위해서는 ModelBase를 상속하는 클래스를 먼저 만들어야 됩니다. 그리고 또한 모델은 클라이언트 사이드 클래스라서 어노테이션(Annotation)을 입력해줘야 합니다. 예제로 간단히 쌍둥이 슬라임을 만들어봅시다.

@SideOnly(Side.CLIENT)

public class ModelTwinSlime extends ModelBase {

 

    public ModelTwinSlime()

{

        

}

 

}

슬라임과 똑같이 만들면 재미가 없으니 이름을 TwinSlime으로 하고 모델링을 시작해 보겠습니다.

 

 

2) 구상하기

가장 먼저 해야 할 것은 구상입니다. 어떤 모습으로 모델링을 할 건지를 먼저 확실하게 떠올려야 작업을 진행할 수 있습니다. 아니면 사이즈, 위치, 텍스쳐 등을 여러 번 수정해야 하기 때문에 굉장히 작업이 더디고 느려지게 됩니다. 이때는 다른 3D 렌더링 프로그램을 이용해서 스케치를 해보는 것도 도움이 됩니다. 추천하는 프로그램은 블렌더나 구글 스케치 업과 같은 무료 프로그램들입니다.

이 정도로 배치할 육면체의 개수와 각각의 위치, 크기, 각도를 명확하게 스케치하면 작업이 차질 없이 진행될 수 있습니다. (위는 블렌더를 이용해서 스케치했습니다.)

 

 

3) 모델링(Modeling)

ModelTwinSlime 클래스 안에 각 육면체에 해당하는 ModelRenderer를 선언하겠습니다. ModelRenderer는 렌더링을 위한 클래스이기도 하면서, 모델링 중에는 각 객체가 하나의 그룹 역할을 하게 됩니다. 이렇게 그룹을 나누는 이유는 애니메이션을 만들 때 ModelRenderer 단위로 회전 시키고, 또한 복사나 반사 대칭과 같은 기능이 제공되어 굉장히 유용하기 때문입니다. 따라서 그룹을 잘 만들면 대상이 복잡해 질수록 작업량을 크게 줄일 수 있습니다.

@SideOnly(Side.CLIENT)

public class ModelTwinSlime extends ModelBase {

 

    private ModelRenderer top;

    private ModelRenderer bot;

    

    public ModelTwinSlime()

{

          

}

 

}

Top이 위에 있는 작은 슬라임을, bot이 아래에 있는 큰 슬라임을 표현할 것입니다.

 

ModelTwinSlime의 생성자에서 이제 top과 bot의 모델링을 진행하여야 합니다. 각각에 대해 새로운 객체를 만듭니다.

    this.top = new ModelRenderer(this,0,0);

    this.bot = new ModelRenderer(this,0,0);

여기서 인자(parameter)에 대해서 설명하자면, 첫 번째는 렌더러의 대상 모델(this), 두 번째와 세 번째는 텍스쳐의 위치(offset)을 의미하는데 이것은 다음의 그림을 참조하여 결정해봅시다.

 

모델 렌더러에 육면체를 추가하여 그리는 경우, 그 전개도는 다음과 같이 됩니다. 하늘색이 앞부분을 의미하며 T는 윗면, B는 아랫면에 해당합니다. 여기서 두 번째, 세 번째 인자는 전개도의 왼쪽 윗 점의 좌표(x, y)를 의미합니다.

 

ModelTwinSlime에서는 bot의 경우 x,z가 1일 때 y가 0.8입니다.(그렇게 구상되었습니다) 이것을 적당히 20배하여 텍스쳐를 제작한다고 가정하면 x, z는 20픽셀, y는 16 픽셀이 됩니다. 따라서 저 전개도의 높이는 36이 되어서 두 번째 육면체에 대한 오프셋은 (0,36)이 되면 알맞게 설정이 됩니다.(텍스쳐 파일 상에서 bot을 위에, top을 아래에 그리겠습니다.) 그리고 또한 전체적으로 필요한 텍스쳐의 크기가 가로는 80, 세로는 50이되므로 그것 또한 적용해주면.

public ModelTwinSlime()

{

    textureWidth = 80;

    textureHeight = 50;

    this.top = new ModelRenderer(this,0,36);

    this.bot = new ModelRenderer(this,0,0);

}

이렇게 됩니다. 텍스쳐 크기를 명확하게 정하지 않으면 렌더링이 이상하게 될 수 있으므로 주의하세요.

 

이제 각각에 적절한 크기의 육면체를 추가합니다. 이때 모두 육면체의 중심을 원점으로 설정해주세요. Top의 경우에는 스케치에서는 그 중심이 원점이 아니지만 회전을 한 후에 추가적으로 옮겨줄 겁니다.

    This.top = new ModelRenderer(this,0,36);

    this.bot = new ModelRenderer(this,0,0);

    bot.addBox(-10, -8, -10, 20, 16, 20);

    top.addBox(-4, -3, -4, 8, 6, 8);

addBox라는 메소드를 이용하는데 각 인자에 대해서 설명을 하면, 123번 인자는 직육면체의 한 모서리의 좌표(좌표축 상에서 x,y,z좌표 값이 가장 작은)이고 456번 인자는 x,y,z축 길이입니다. 여기서 주의할 점은 y좌표 값이 작아질수록 실제 게임에서는 위쪽에 그려진다는 것입니다. 박스를 추가하는데 필요한 좌표와 길이는 처음에 스케치를 했던 블렌더에서 몇 가지 정보를 가져와 addBox의 인자가 요구하는 값으로 변환시킨 것입니다.

 

이제 top을 Y축에 대해서 45도 회전시킨 다음에, 우리가 블렌더에서 계획했던 위치로 평행이동 시키겠습니다. Top과 bot의 높이의 합이 11이기 때문에 높이 방향(Y)으로는 11만큼 평행이동 시켰습니다.

    this.top = new ModelRenderer(this,0,36);

    this.bot = new ModelRenderer(this,0,0);

    bot.addBox(-10, -8, -10, 20, 16, 20);

    top.addBox(-4, -3, -4, 8, 6, 8);

    

    top.rotateAngleY = 0.25f*(float)Math.PI;

    top.setRotationPoint(3, -11, 3);

rotateAngleY는 Y축을 중심으로 대상을 얼만큼 회전시키는 가에 대한 변수인데, 라디안(Radian)단위이므로 180도 = 1파이 라는 것을 유의하세요. setRotationPoint는 회전 후에 대상을 평행이동 하게 합니다.

이렇게 하면 우리가 최초에 스케치했던 모양대로 모델링이 완성됩니다.

 

그러나 여기서 한 가지 고려해야 할 점은 실제 엔티티의 바운딩 박스(bounding box)의 가장 아래 부분의 좌표가 y=24라는 것 입니다. 즉, 지금 위처럼 박스들의 y좌표를 설정하면 실제로 엔티티가 붕 뜬 것처럼 나오게 됩니다.

이렇게 말이죠. F3 + B를 눌러보면 실제 엔티티의 바운딩 박스를 확인해 볼 수 있는데,

보이는 바와 같이 훨씬 아랫쪽에 바운딩 박스가 있습니다. 이 문제를 해결하기 위해서 각 박스(top, bot)의 위치를 y=24에 맞추어서 평행이동 해줘야 합니다.

public ModelTwinSlime()

{

    textureWidth = 80;

    textureHeight = 50;

    this.top = new ModelRenderer(this,0,36);

    this.bot = new ModelRenderer(this,0,0);

    bot.addBox(-10, 8, -10, 20, 16, 20);

    top.addBox(-4, 13, -4, 8, 6, 8);

 

    top.rotateAngleY = 0.25f*(float)Math.PI;

    top.setRotationPoint(3, -11, 3);

}

이렇게 맞추어 주시면 정확하게 렌더링 됩니다. (지금 슬라임에게 텍스쳐가 있는 이유는 제가 붙여놨기 때문이고, 아직까지 강의에는 그 방법이 나오지 않았습니다. 조금 더 읽어보세요)

 

마지막으로 render 메소드를 오버라이드해서 top과 bot을 렌더링하도록 해야 합니다.

@SideOnly(Side.CLIENT)

public class ModelTwinSlime extends ModelBase {

 

    private ModelRenderer top;

    private ModelRenderer bot;

    

    public ModelTwinSlime()

{

        textureWidth = 80;

        textureHeight = 50;

        this.top = new ModelRenderer(this,0,36);

        this.bot = new ModelRenderer(this,0,0);

        bot.addBox(-10, 8, -10, 20, 16, 20);

        top.addBox(-4, 13, -4, 8, 6, 8);

 

        top.rotateAngleY = 0.25f*(float)Math.PI;

        top.setRotationPoint(3, -11, 3);

}

 

    public void render(Entity par1Entity, float par2, float par3, float par4,

            float par5, float par6, float par7) {

        

        this.top.render(par7);

        this.bot.render(par7);

        

    }

}

 

 

2. 렌더 클래스 만들기

 

1) 렌더 클래스 만들기

이제 텍스쳐(Texture)를 설정해주는 렌더 클래스를 만들어보겠습니다. 렌더 클래스는 RenderLiving을 상속하게 하여 만들게 됩니다. 텍스쳐가 변하지 않는 경우는 상당히 간단하게 끝납니다.

public class RenderTwinSlime extends RenderLiving {

 

    protected ResourceLocation texture;

    

    public RenderTwinSlime(ModelBase p_i1262_1_, float p_i1262_2_) {

        super(p_i1262_1_, p_i1262_2_);

        texture = new ResourceLocation("hungryanimals:textures/entities/twinslime.png");

        // TODO Auto-generated constructor stub

    }

    

    @Override

    protected ResourceLocation getEntityTexture(Entity p_110775_1_) {

        // TODO Auto-generated method stub

        return texture;

    }

 

}

여기서 중요한 것은 png파일의 경로입니다. "…\Main\resources\assets\hungryanimals\textures\entities\twinslime.png"가 실제 경로고, assets까지의 코드를 인식하기 때문에 우리가 입력해야 하는 경로의 형식은 위에 코드와 같습니다.

 

2) 텍스쳐(Texture) 만들기

텍스쳐는 항상 아래의 그림과, 우리가 코드상에서 설정한 텍스쳐 오프셋을 잘 고려하여 찍어주시면 됩니다. 파일명은 렌더 클래스에서 설정한 것과 같이 twinslime.png여야 하고, 각 전개도의 위치도 위와 같아야 합니다.

이런 식으로 말이죠 아래의 사진은 실제 png파일입니다.

 

 

3. 엔티티 클래스 만들기

 

엔티티 클래스는 딱히 열심히 만들지는 않을 것입니다. 우리가 렌더링이 잘 되는 지 확인하기 위한 용도이므로, 다음의 코드를 복사만 하셔도 좋습니다. 다만, 중간의 setSize()는 주목하셔야 합니다. setSize는 바운딩 박스의 크기를 변환시켜주는 메소드로써 첫 번째 인자가 바운딩 박스의 xz축 길이, 두 번째 인자가 y축 길이입니다.(단위는 픽셀이 아닌, 마인크래프트 월드상의 좌표와 같습니다. 즉 1이 한 블록 크기입니다.)

public class EntityTwinSlime extends EntityLiving {

    public EntityTwinSlime(World p_i1582_1_) {

        super(p_i1582_1_);

        setSize(1.25f, 1f);

    }

}

바운딩 박스의 크기가 알맞게 설정된 것을 확인할 수 있습니다.

 

 

4. 엔티티 등록하기 & 소환하여 확인하기

 

이 부분은 이전 강좌의 내용과 100% 일치합니다. 코드와 사진만 붙이겠습니다.

@Mod.EventHandler

public static void preInit(FMLPreInitializationEvent event) {

    

    EntityRegistry.registerModEntity(EntityTwinSlime.class, "twinSlime", 5, ExampleMod.instance, 80, 3, false);

    

    if (proxy instanceof ClientProxy) {

     RenderingRegistry.registerEntityRenderingHandler(EntityTwinSlime.class, new RenderTwinSlime(new ModelTwinSlime(),1f));

}

}

 

 

끝. 귀엽네요. 엔티티 클래스에 내용이 없어 숨만 쉴 뿐 아무것도 하지 않습니다. 여러분이 어떤 내용을 짜 넣는가에 따라 행동이 달라지겠지만 그와 관련된 내용은 다음 강의부터 다뤄보도록 하겠습니다. 질문 있으시면 댓글로 달아주세요.

 

 

 

+ Recent posts