Using Your Techne Model

From Minecraft Forge
Jump to: navigation, search

In this tutorial, we will cover the things you can do with your Techne model. This tutorial is work-in-progress, and is just a placeholder. Only mobs are covered currently. This tutorial was written with 1.4.7 in mind, but everything should work for 1.5.x

Contents

Prerequisites

Know how to Model in Techne Have a ready-made model in Techne (Or the .java of it)


Mobs

The most common thing people model in Techne is a mob, so we'll cover that first. All examples will be taken from my mod Remula.

The Model File

The first thing you will need is your model file. Create a new class called ModelName, replacing Name with the name of your mob. Now, copy and paste everything from the .java file you got from Techne into your new class. There's a few things you need to do right now:

  • Set your package. Techne defaults to a minecraft package, so change it to yours.
  • Imports. You need to import ModelBase, ModelRenderer, Entity and possibly MathHelper too.
  • Fix a techne bug.

Techne has a slight bug that it misses a few Entity references towards the bottom. Scroll right down, and find the "public void setRotationAngles" section. Change it, so it looks like this:

  public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity)
  {
    super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);

Now, scroll up a few lines, and you'll see a setRotationAngles with an error on. Again, just add "entity" after f5.

That's all you need for now, but we'll come back to the model file later for rotation code.

The Entity File

Next, we're going to make your Entity file. This is where you specify your mob's AI, and what it can do. Relax, it's really quite straight-forward. The easiest thing for me to do here is to post a heavily annotated example class, so here it is. Note this is for a friendly critter, not a hostile mob, although there isn't a huge difference other than the AI tasks.

package co.uk.silvania.Remula.entity.akatoe;

--Imports removed to keep it short--

//Your declaration. If your mob swims, change EntityAnimal to EntityWaterMob.
public class EntityGlog extends EntityAnimal {
       
        public EntityGlog(World par1World) {
                super(par1World);
                this.texture = "/co/uk/silvania/Remula/resources/mobglog.png";
                //The below means if possible, it wont walk into water
                this.getNavigator().setAvoidsWater(true);
                //This is the hitbox size. I believe it starts in the center and grows outwards
                this.setSize(1.5F, 0.9F);
                //Pretty self-explanatory.
                this.isImmuneToFire = false;
                float var2 = 0.25F;

                //Now, we have the AI. Each number in the addTask is a priority. 0 is the highest, the largest is lowest.
                //They should be set in the order which the mob should focus, because it can only do one thing at a time. I'll explain my choice for order below.
                //There are tonnes of tasks you can add. Look in the JavaDocs or other mob classes to find some more!

                //Swimming should ALWAYS be first. Otherwise if your mob falls in water, but it's running away from you or something it'll drown.
                this.tasks.addTask(0, new EntityAISwimming(this));

                //This makes the mob run away when you punch it
                this.tasks.addTask(1, new EntityAIPanic(this, 0.38F));

                //If you have mating code, this allows it to mate.
                this.tasks.addTask(2, new EntityAIMate(this, var2));

                //This code is used to get the mob to follow you (like cows with wheat). Here it's set to a custom fruit
                this.tasks.addTask(3, new EntityAITempt(this, 0.3F, Remula.porinFruit.itemID, false));

                //If the mob is a child, it will follow it's parent.
                this.tasks.addTask(4, new EntityAIFollowParent(this, 0.28F));

                //This makes the mob walk around. Without it, it'd just stand still.
                this.tasks.addTask(5, new EntityAIWander(this, var2));
 
                //This makes the mob watch the nearest player, within a range set by the float.
                this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));

                //Finally, this makes it look around when it's not looking at a player or wandering.
                this.tasks.addTask(7, new EntityAILookIdle(this));
        }

        //This is required. If it's false, none of the above takes effect.
        public boolean isAIEnabled() {
                return true;
        }
       
        //Pretty obvious, set it's health!
        public int getMaxHealth() {
                return 10;
        }
       
        //The sound effect played when it's just living, like a cow mooing.
        protected String getLivingSound() {
                return "mob.glog.say";
        }
       
        //The sound made when it's attacked. Often it's the same as the normal say sound, but sometimes different (such as in the ender dragon)
        protected String getHurtSound() {
                return "mob.glog.say";
        }
       
        //The sound made when it actually dies.
        protected String getDeathSound() {
                return "mob.glog.death";
        }

        //The sound the mob plays when walking around. 
        protected void playStepSound(int par1, int par2, int par3, int par4) {
                this.worldObj.playSoundAtEntity(this, "mob.glog.step", 0.15F,  1.0F);
        }
       
        //A basic example of what a mob should drop on death. For more advanced examples, look at code for chicken or squid.
        protected int getDropItemId() {
                return Remula.rawGlogMeat.itemID;
        }
       
        //This is required regardless of if your animal can breed or not. Set to null if it can't breed - I wont cover breeding here.
        public EntityAgeable createChild(EntityAgeable var1) {
                return null;
        }
}

See, nice and easy! Have a tinker, change some settings, then we'll move on.

Mob Render File

The last of the three classes you need to create is the Render file.

There isn't much at all to explain in this one really. Providing your mob is relatively simple, you can just use this code, from the RenderCow:

package net.minecraft.client.renderer.entity;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.model.ModelBase;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.passive.EntityCow;

@SideOnly(Side.CLIENT)
public class RenderCow extends RenderLiving
{
    public RenderCow(ModelBase par1ModelBase, float par2)
    {
        super(par1ModelBase, par2);
    }

    public void renderCow(EntityCow par1EntityCow, double par2, double par4, double par6, float par8, float par9)
    {
        super.doRenderLiving(par1EntityCow, par2, par4, par6, par8, par9);
    }

    public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9)
    {
        this.renderCow((EntityCow)par1EntityLiving, par2, par4, par6, par8, par9);
    }

    /**
     * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then
     * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic
     * (Render<T extends Entity) and this method has signature public void doRender(T entity, double d, double d1,
     * double d2, float f, float f1). But JAD is pre 1.5 so doesn't do that.
     */

    public void doRender(Entity par1Entity, double par2, double par4, double par6, float par8, float par9)
    {
        this.renderCow((EntityCow)par1Entity, par2, par4, par6, par8, par9);
    }
}

If your mob does something more advanced, I suggest just reading through the net.minecraft.client.renderer.entity package for examples.

The Client Proxy

You need to add a few things into your client proxy. First, we'll register the renderer:

        public void registerRenderInformation() {
                RenderingRegistry.registerEntityRenderingHandler(EntityName.class, new RenderName(new ModelName(), 0.5F));

As you can see, that single line of code ties together all three of the classes you've made. It get's the Entity class, finds the class on how to render it and also finds the model to be rendered. Finally, the float at the end is the shadow size- 0.5 is about right for a player-sized model, but remember the shadow should be based on the horizontal size, not the vertical.

Now, you need to register the actual mob. Mob's have IDs, just like blocks. Luckily, there is a nice little line we can put which will automagically find an empty slot, and use it.

                EntityRegistry.registerGlobalEntityID(EntityName.class, "yourMob", EntityRegistry.findGlobalUniqueEntityId(), 3515848, 12102);

First, you reference your Entity class again. Then, set a localized name. The third section is that automagical thing I just mentioned, and finally you have the egg colours. Those two numbers are hex values for the background and spots of the mob egg. change them to whatever you like, or remove them if you don't want an egg. Without those two numbers, there will be no mob egg.

The Base Mod File

There isn't much at all to do in here, just 2-3 lines per entity, depending on if you want it to naturally spawn or not. All lines go into your @Init:

            EntityRegistry.registerModEntity(EntityName.class, "yourMob", 2, this, 80, 3, true);
            EntityRegistry.addSpawn(EntityName.class, 5, 2, 6, EnumCreatureType.creature, Remula.akatoePlainsBiome, BiomeGenBase.plains);
            LanguageRegistry.instance().addStringLocalization("entity.instance.yourMob.name", "Your Mob");

The first line is the rest of the registry. You need to again specify your Entity class and localized name. The "2" in the example is a local mob ID; it needs to be set differently for each mob within your mod, but wont conflict with other mods. "this" simply refers to the fact it's in this mod. the 80 is the tracking frequency; 80 is actually quite high. This is how far the mob will see/follow a target. The 3 is the update frequency, and I believe this is in ticks. This is how often the mob checks what it should be doing from it's AI list. Finally, the "true" is whether it sends velocity updates. Honestly, I don't quite know what this does, but I think it means the mob can push you.

The second line adds the mob spawning to the world, and is optional. If your mob shouldn't naturally spawn, remove it! EntityName is the class again, and then you have 3 numbers. The 5 is it's probability of spawning - You can use this with reference to other mobs to decide which has more or less chance of spawning. The 2 is the lowest amount of a group, and 6 is the highest, meaning the mob will spawn in groups of 2-6. EnumCreatureType decides precisely where it can spawn; Creature will spawn in open sunlight, Monster will spawn in darkness and waterCreature will spawn in water. Finally, you have a biome list. You can list as many biomes as you want. I've put one normal and one custom as an example. The BiomeGenBase class lists all the biomes, if you need a list.

The last line, honestly I couldn't get it to work but I'll explain anyway. This sets the name of your mob to be localized. It's very simple really, you only have two parameters. The first should be "entity." followed by your mod's instance, followed by the unlocalized name, followed by ".name". The second is how you want the name to appear in-game. I got the code from another tutorial I followed when I was learning this myself, so it may be a 1.5 feature (which I'm not using). Post your results in the discussion page please!

If you test the game now, the mob egg should be in your Miscellaneous tab, and you can spawn it. It will glide eerily around the land without moving a limb... because we've not animated it yet!

Back to the Model Class

Remember earlier I said we'll come back later for Animation? I always find it's best to get the mob working before you animate. That way, if there's an issue in your Model class which stops the mod loading, you won't have to remake your animation code. So, assuming everything is working, it's time to animate!

Before we start, it's very important that you have correctly set your rotation points as specified in the Modelling in Techne tutorial. If you didn't weird things will happen! It's also very helpful if you have sensible shape names, so you know which part is what just from the name. If you didn't name them, just use Techne as a reference.

Ok, the rotation code for a basic mob is surprisingly simple. Go down to the very end, where your public void setRotationAngles is, and add some lines under the super like this:

  public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity)
  {
    super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
   
    this.head.rotateAngleX = f4 / (180F / (float)Math.PI);
    this.head.rotateAngleY = f3 / (180F / (float)Math.PI);
    this.leg1.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;
    this.leg2.rotateAngleX = MathHelper.cos + (float)Math.PI) * 1.4F * f1;
    this.arm2.rotateAngleX = MathHelper.cos + (float)Math.PI) * 1.4F * f1;
    this.arm1.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;

Ok, lets quickly run over this. this.part.rotateAngleX = "part" is the shape to be moved. For example above, there is head, two legs and two arms. The rotateAngleX specifies the axis it should move on; X, Y or Z. In the above example, a head can "nod" or "shake", by moving on X and Y. The only other things you need to really worry about in here are the two floats on the arms/legs: The float 0.6662F/float Math.PI are swapped to alter the direction that the part moves in. the Math.PI lines are perfectly opposite. In the above example, when his left leg goes forward, his right arm will also go forward, and his left arm and right leg will go backwards. The float which is currently at 1.4F on all limbs is the amount it should move by. If you find the mobs limbs move too far, try lowering it. If they're not going far enough, raise it.

And that's all there is to it! For reference, here's the finished Model file from one of my mobs. I chose a simple one to keep it easy:


package co.uk.silvania.Remula.client.models;

import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;

public class ModelGlog extends ModelBase
{
  //fields
    ModelRenderer head;
    ModelRenderer body;
    ModelRenderer leg1;
    ModelRenderer leg2;
    ModelRenderer leg3;
    ModelRenderer leg4;
    ModelRenderer leg5;
    ModelRenderer leg6;
 
  public ModelGlog()
  {
    textureWidth = 64;
    textureHeight = 64;
   
      head = new ModelRenderer(this, 0, 0);
      head.addBox(-4F, -4F, -8F, 8, 8, 8);
      head.setRotationPoint(0F, 12F, -6F);
      head.setTextureSize(64, 64);
      head.mirror = true;
      setRotation(head, 0F, 0F, 0F);
      body = new ModelRenderer(this, 0, 26);
      body.addBox(-5F, -10F, -7F, 12, 24, 8);
      body.setRotationPoint(-1F, 11F, 3F);
      body.setTextureSize(64, 64);
      body.mirror = true;
      setRotation(body, 1.570796F, 0F, 0F);
      leg1 = new ModelRenderer(this, 0, 16);
      leg1.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg1.setRotationPoint(-4F, 18F, 15F);
      leg1.setTextureSize(64, 64);
      leg1.mirror = true;
      setRotation(leg1, 0F, 0F, 0F);
      leg2 = new ModelRenderer(this, 0, 16);
      leg2.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg2.setRotationPoint(4F, 18F, 15F);
      leg2.setTextureSize(64, 64);
      leg2.mirror = true;
      setRotation(leg2, 0F, 0F, 0F);
      leg3 = new ModelRenderer(this, 0, 16);
      leg3.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg3.setRotationPoint(-4F, 18F, -5F);
      leg3.setTextureSize(64, 64);
      leg3.mirror = true;
      setRotation(leg3, 0F, 0F, 0F);
      leg4 = new ModelRenderer(this, 0, 16);
      leg4.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg4.setRotationPoint(4F, 18F, -5F);
      leg4.setTextureSize(64, 64);
      leg4.mirror = true;
      setRotation(leg4, 0F, 0F, 0F);
      leg5 = new ModelRenderer(this, 0, 16);
      leg5.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg5.setRotationPoint(-4F, 18F, 5F);
      leg5.setTextureSize(64, 64);
      leg5.mirror = true;
      setRotation(leg5, 0F, 0F, 0F);
      leg6 = new ModelRenderer(this, 0, 16);
      leg6.addBox(-2F, 0F, -2F, 4, 6, 4);
      leg6.setRotationPoint(4F, 18F, 5F);
      leg6.setTextureSize(64, 64);
      leg6.mirror = true;
      setRotation(leg6, 0F, 0F, 0F);
  }
 
  public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5)
  {
    super.render(entity, f, f1, f2, f3, f4, f5);
    setRotationAngles(f, f1, f2, f3, f4, f5, entity);
    head.render(f5);
    body.render(f5);
    leg1.render(f5);
    leg2.render(f5);
    leg3.render(f5);
    leg4.render(f5);
    leg5.render(f5);
    leg6.render(f5);
  }
 
  private void setRotation(ModelRenderer model, float x, float y, float z)
  {
    model.rotateAngleX = x;
    model.rotateAngleY = y;
    model.rotateAngleZ = z;
  }
 
  public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity)
  {
    super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
   
    this.head.rotateAngleX = f4 / (180F / (float)Math.PI);
    this.head.rotateAngleY = f3 / (180F / (float)Math.PI);
    this.body.rotateAngleX = ((float)Math.PI / 2F);
    this.leg1.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;
    this.leg2.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;
    this.leg3.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;
    this.leg4.rotateAngleX = MathHelper.cos(f * 0.6662F + (float)Math.PI) * 1.4F * f1;
    this.leg5.rotateAngleX = MathHelper.cos(f * 0.6662F + (float)Math.PI) * 1.4F * f1;
    this.leg6.rotateAngleX = MathHelper.cos(f * 0.6662F + (float)Math.PI) * 1.4F * f1;
  }

}


Non-mob entites

I'm guessing Nait will be able to write this. Covers things like boats, carts, trains.

Custom-Rendered Block

As a tile entity

For example: Custom_Tile_Entity_Renderer

Without using a tile entity

I hear it's possible. Other than that, see "As a tile entity"

Personal tools
Namespaces
Variants
Actions
Navigation
tutorials
Toolbox