In this tutorial, we will make a custom, Item-based projectile, a throwing rock. It covers all the steps necessary to add any projectile entity into the game, including how to get it to render on the screen properly.
For this tutorial, we're going to make a new item ThrowingRock and get it to render a custom texture when thrown. We'll assume you've got your main mod space set up. If not, I HIGHLY suggest you go over to TechGuy543's tutorial at http://www.minecraft...ding-tutorials/
and make sure you set up your main mod correctly or you will NOT be able to get your custom item rendering.
STEP 1: Creating a custom Item, e.g. ItemThrowingRock
Like I said, we're going to make a ThrowingRock. To do this, we just make a new class called ThrowingRock in one of our packages. This class needs to exend Item, but we're going to make it extend another class, BaseModItem, which extends Item. The magic of inheritance will allow our ThrowingRock to extend Item through BaseModItem. Here is BaseModItem's code:
public class BaseModItem extends Item
{
// no more IDs
public BaseModItem() {
super();
}
// IconRegister renamed to IIconRegister
@Override
@SideOnly(Side.CLIENT)
public void registerIcons(IIconRegister iconRegister) {
itemIcon = iconRegister.registerIcon("tutorial:" + getUnlocalizedName().substring(5).toLowerCase());
}
}
This allows us to avoid re-writing the registerIcon function in every single new item we make. Nifty. Now here's what ItemThrowingRock will look like:
public class ItemThrowingRock extends BaseModItem {
// no more ID parameter
public ItemThrowingRock()
{
super();
// we'll set the max stack size and creative tab from here:
setMaxStackSize(18);
setCreativeTab(CreativeTabs.tabCombat);
}
}
We set the maxStackSize and CreativeTab in the constructor above, but you could also do it when you declare the item in your main mod instance like so:
public static Item throwingRock;
// ALL Blocks, Items, etc. must be initialized during FML Pre-Initialization Event
@EventHandler
public void preInit(FMLPreInitializationEvent event) {
throwingRock = new ItemThrowingRock().setUnlocalizedName("throwingRock").
setMaxStackSize(18).setCreativeTab(CreativeTabs.tabCombat);
}
So far our ThrowingRock doesn't do anything. We want this class to throw rocks, much like snowballs, so we'll steal ItemSnowball's onImpact function and paste it into our ItemThrowingRock class:
/**
* Called whenever this item is equipped and the right mouse button is pressed.
* Args: itemStack, world, entityPlayer
*/
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
if (!player.capabilities.isCreativeMode) {
--stack.stackSize;
}
world.playSoundAtEntity(player, "random.bow", 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
// IMPORTANT! Only spawn new entities on the server. If the world is not remote,
// that means you are on the server:
if (!world.isRemote) {
world.spawnEntityInWorld(new EntityThrowingRock(world, player));
}
return stack;
}
Ok, run the code and you'll see your ThrowingRocks spawn snowballs. That's not what we want. Change "EntitySnowball" to "EntityThrowingRock." You'll get an error, but we'll fix that in step 2.
STEP 2: Creating a custom Entity, e.g. EntityThrowingRock
Ok, we want our ThrowingRock to behave similarly to snowballs, so let's look at that class.
public class EntitySnowball extends EntityThrowable
{
public EntitySnowball(World par1World)
{
super(par1World);
}
public EntitySnowball(World par1World, EntityLivingBase par2EntityLivingBase)
{
super(par1World, par2EntityLivingBase);
}
public EntitySnowball(World par1World, double par2, double par4, double par6)
{
super(par1World, par2, par4, par6);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
protected void onImpact(MovingObjectPosition movObjPos)
{
if (movObjPos.entityHit != null)
{
byte b0 = 0;
if (movObjPos.entityHit instanceof EntityBlaze)
{
b0 = 3;
}
movObjPos.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, this.getThrower()), (float)b0);
}
for (int i = 0; i < 8; ++i)
{
this.worldObj.spawnParticle("snowballpoof", this.posX, this.posY, this.posZ, 0.0D, 0.0D, 0.0D);
}
if (!this.worldObj.isRemote)
{
this.setDead();
}
}
}
Well, we can just copy and paste this into our mod package and rename it to EntityThrowingRock. Perfect. Here are some ways you can tweak it to make it more rock-like:
Tweak 1: Do you see "byte b0" in the code? That's how much damage our entity will inflict. For snowballs, it is zero unless you hit a blaze. Well, we don't need that, so delete all that stuff about the blaze as well as the related import. Let's rename "byte b0" to "float rockDamage." Now it's obvious what it does. Now you have to change "(float)b0" to "rockDamage."
Tweak 2: Change the damage. Right now, it says "float rockDamage = 0"; let's change it to 2 so as not to make it too powerful, but you can change it to whatever you want.
Tweak 3: Our rock spawns snowballpoofs on impact. Lame. Just change "snowballpoof" to "crit" for now. It won't look awesome, but it's better than snow.
Here's what our new EntityThrowingRock code looks like, with renamed method parameters:
public class EntityThrowingRock extends EntityThrowable {
public EntityThrowingRock(World world) {
super(world);
}
public EntityThrowingRock(World world, EntityLivingBase entity) {
super(world, entity);
}
public EntityThrowingRock(World world, double x, double y, double z) {
super(world, x, y, z);
}
/**
* Called when this EntityThrowable hits a block or entity.
*/
@Override
protected void onImpact(MovingObjectPosition mop) {
if (mop.entityHit != null) {
// We changed this to type 'float' and set to '2'; note you could just put the damage in
// the method directly if you don't intend to change the damage variable
float rockDamage = 2;
// now in this line we don't need to cast as 'float', since our variable is already that type
mop.entityHit.attackEntityFrom(DamageSource.causeThrownDamage(this, this.getThrower()), rockDamage);
}
// spawn 4 "crit" particles at the point of impact
for (int l = 0; l < 4; ++l) {
this.worldObj.spawnParticle("crit", this.posX, this.posY, this.posZ, 0.0D, 0.0D, 0.0D);
}
// be sure to set the entity to 'dead' or it will keep updating forever and you'll end up with lots of
// leftover entities in your world
if (!worldObj.isRemote) {
setDead();
}
}
}
Note the "@Override" before our onImpact function - it's not critical here, as EntityThrowable.onImpact() doesn't have anything in it, but it's good practice to put this here; if the method name or signature ever changes in a Minecraft update, you will know immediately because your old method will give you an error. This means you need to find the new method signature and change your method name - simply removing @Override will get rid of the error, too, but then your method will never be called!
STEP 3: Registering your custom entity and renderer.
We've set up our custom Entity class, but Minecraft doesn't know about it yet. So we have to let it know in using EntityRegistry.registerModEntity in our main mod load method and tell it what renderer to use using RenderingRegistry.registerEntityRenderingHandler in our ClientProxy. Here's how:
First in your main mod class:
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
// Config, Blocks, Items, etc. go up here
throwingRock = new ItemThrowingRock().setUnlocalizedName("throwingRock");
// I like using an incrementable index to set my IDs rather than writing 1, 2, 3, etc., so I never have
// to worry about order or if I missed a number (doesn't really matter though)
int modEntityID = 0;
// If you have a lot of Entities to register, consider creating a class with an 'registerEntities' method
// so your main class stays tidy and readable
EntityRegistry.registerModEntity(EntityThrowingRock.class, "Throwing Rock", ++modEntityID, this, 64, 10, true);
// Now that we've registered the entity, tell the proxy to register the renderer
proxy.registerRenderers();
}
Now to the ClientProxy:
public class ClientProxy extends CommonProxy {
@Override
public void registerRenderers() {
RenderingRegistry.registerEntityRenderingHandler(EntityThrowingRock.class, new RenderSnowball(YourModName.throwingRock));
}
}
registerModEntity(...) takes the class, a name (just make it up here), a unique ID, the instance of YourMod, tracking range, frequency of tracking updates, and whether to send velocity information.
RenderSnowball(...) requires the item to be rendered as an argument; since we haven't declared any items locally, we use the declaration from the instance of our mod.
NOTE: A good reference to decide what tracking update frequency to use: https://docs.google...output=html (from the post mentioned in the previous note). For projectiles, ranges of 10-20 are the norm.
NOTE: If you want your custom item to render differently from a vanilla item, you will need to make a custom render class. See the next step.
Your custom entity should now render correctly in the world! Congratulations!
STEP 4: Rendering: The Power of Inheritance
Okay, so you've got everything working correctly, but you want your Item to exhibit some custom behavior when it renders. We need to create a new Render class. The easiest way to do this is to copy from an existing Renderer and paste it into your new class. In our case, ThrowingRock behaves like Snowballs, so we will copy RenderSnowball and rename it to RenderThrowingRock:
@SideOnly(Side.CLIENT)
public class RenderThrowingRock extends Render
{
private Item field_94151_a;
private int field_94150_f;
public RenderThrowingRock(Item par1Item, int par2)
{
this.field_94151_a = par1Item;
this.field_94150_f = par2;
}
public RenderThrowingRock(Item par1Item)
{
this(par1Item, 0);
}
/**
* 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)
{
Icon icon = this.field_94151_a.getIconFromDamage(this.field_94150_f);
if (icon != null)
{
GL11.glPushMatrix();
GL11.glTranslatef((float)par2, (float)par4, (float)par6);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glScalef(0.5F, 0.5F, 0.5F);
// this.func_110777_b(par1Entity); // worked in Forge 804, but no longer; use this:
this.bindEntityTexture(par1Entity);
Tessellator tessellator = Tessellator.instance;
// You can remove this whole section if you want, it's just for Potions
if (icon == ItemPotion.func_94589_d("bottle_splash"))
{
int i = PotionHelper.func_77915_a(((EntityPotion)par1Entity).getPotionDamage(), false);
float f2 = (float)(i >> 16 & 255) / 255.0F;
float f3 = (float)(i >> 8 & 255) / 255.0F;
float f4 = (float)(i & 255) / 255.0F;
GL11.glColor3f(f2, f3, f4);
GL11.glPushMatrix();
this.func_77026_a(tessellator, ItemPotion.func_94589_d("overlay"));
GL11.glPopMatrix();
GL11.glColor3f(1.0F, 1.0F, 1.0F);
}
this.func_77026_a(tessellator, icon);
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
GL11.glPopMatrix();
}
}
// For Forge 804:
/*
protected ResourceLocation func_110775_a(Entity par1Entity)
{
return TextureMap.field_110576_c;
}
*/
// For Forge 871:
@Override
protected ResourceLocation getEntityTexture(Entity entity) {
return TextureMap.locationItemsTexture;
}
private void func_77026_a(Tessellator par1Tessellator, Icon par2Icon)
{
float f = par2Icon.getMinU();
float f1 = par2Icon.getMaxU();
float f2 = par2Icon.getMinV();
float f3 = par2Icon.getMaxV();
float f4 = 1.0F;
float f5 = 0.5F;
float f6 = 0.25F;
GL11.glRotatef(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(-this.renderManager.playerViewX, 1.0F, 0.0F, 0.0F);
par1Tessellator.startDrawingQuads();
par1Tessellator.setNormal(0.0F, 1.0F, 0.0F);
par1Tessellator.addVertexWithUV((double)(0.0F - f5), (double)(0.0F - f6), 0.0D, (double)f, (double)f3);
par1Tessellator.addVertexWithUV((double)(f4 - f5), (double)(0.0F - f6), 0.0D, (double)f1, (double)f3);
par1Tessellator.addVertexWithUV((double)(f4 - f5), (double)(f4 - f6), 0.0D, (double)f1, (double)f2);
par1Tessellator.addVertexWithUV((double)(0.0F - f5), (double)(f4 - f6), 0.0D, (double)f, (double)f2);
par1Tessellator.draw();
}
}
Pure copy-paste. Genius. Be sure to import everything necessary. But now I'll let you in on an even BETTER way, that you should use whenever you can: make a new class that EXTENDS whatever class you want to emulate, rather than copying and pasting:
@SideOnly(Side.CLIENT)
public class RenderThrowingRock extends RenderSnowball
{
public RenderThrowingRock(Item item) {
this(item, 0);
}
public RenderThrowingRock(Item item, int par2) {
super(item, par2);
}
// now you can override the render methods if you want
// call super to get the original functionality, and/or add some stuff of your own
// I'll leave that up to you to experiment with
}
Wow, that's so much simpler. You barely have to do anything. Remember that for the future. Of course you don't really have to even create a Render class for the throwing rock, as you can just pass the Item directly to RenderSnowball when you register, but you can use this technique in many places, overriding methods to add new functionality while getting the benefits of the old.
Now, in our ClientProxy, we need to change this:
RenderingRegistry.registerEntityRenderingHandler(EntityThrowingRock.class, new [s]RenderSnowball[/s](YourModName.throwingRock));
to this:
RenderingRegistry.registerEntityRenderingHandler(EntityThrowingRock.class, new RenderThrowingRock(YourModName.throwingRock));
Or if not using a custom render, use RenderSnowball with your Item as the parameter:
RenderingRegistry.registerEntityRenderingHandler(EntityThrowingRock.class, new RenderSnowball(YourModName.throwingRock));
Now all you need to do is play around with the variables and see what you can get it to do.
STEP 5: Rendering a Model
If you want to do some different rendering than that offered by RenderSnowball, for example if you want a model, you will need to create a new class that extends Render, or one of Render's subclasses.
You will need a ResourceLocation for your texture; this takes 2 parameters: ModId and the path to your texture at the location: "src/minecraft/assets/modid/"
You can also set it with a single parameter, the string of the path to the texture:
"mymodid:textures/entity/mytexture.png" or (mymodid + ":textures/entity/mytexture.png").
In any class that extends Render, or TileEntitySpecialRenderer, you can bind the texture simply by using "this.bindTexture(yourResourceLocation);"
On to the class itself:
@SideOnly(Side.CLIENT)
public class RenderCustomEntity extends Render
{
// ResourceLocations are typically static and final, but that is not an absolute requirement
private static final ResourceLocation texture = new ResourceLocation("yourmodid", "textures/entity/yourtexture.png");
// if you want a model, be sure to add it here:
private ModelBase model;
public RenderCustomEntity() {
// we could have initialized it above, but here is fine as well:
model = new ModelCustomEntity();
}
@Override
protected ResourceLocation getEntityTexture(Entity entity) {
// this method should return your texture, which may be different based
// on certain characteristics of your custom entity; if that is the case,
// you may want to make a second method that takes your class:
return getCustomTexture((CustomEntity) entity);
}
private ResourceLocation getCustomTexture(CustomEntity entity) {
// now you have access to your custom entity fields and methods, if any,
// and can base the texture to return upon those
return texture;
}
// in whatever render method you are using; this one is from Render class:
@Override
public void doRender(Entity entity, double x, double y, double z, float yaw, float partialTick) {
// again, if you need some information from your custom entity class, you can cast to your
// custom class, either passing off to another method, or just doing it here
// in this example, it is not necessary
// if you are going to do any openGL matrix transformations, be sure to always Push and Pop
GL11.glPushMatrix();
// bind your texture:
bindTexture(texture);
// do whatever transformations you need, then render
// typically you will at least want to translate for x/y/z position:
GL11.glTranslated(x, y, z);
// if you are using a model, you can do so like this:
model.render(entity, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0625F);
// note all the values are 0 except the final argument, which is scale
// vanilla Minecraft almost excusively uses 0.0625F, but you can change it to whatever works
GL11.glPopMatrix();
}
}
STEP 6: HELP!!! A.k.a Common Problems and Troubleshooting.
First, please read ALL sections of the above tutorial carefully. Then see if you have done all of the below:
1. Read ALL sections of the tutorial carefully.
2. Make sure you have created your texture using GIMP, Paint, whatever and saved in .png format.
3. Make sure your texture is in the correct folder; for forge, it is:
/forge/mcp/src/minecraft/assets/yourmodid/textures/items/
4. Make sure your folder "yourmodid" is all lower-case.
5. Re-read the tutorial. Did you follow ALL of the steps EXACTLY?
6. Did you double-check that your Main Mod, CommonProxy and ClientProxy are all set up properly?
Check TechGuy543's tutorial one more time to be sure.
Other problems will be noted below. I will update this section if anyone has further problems that aren't addressed elsewhere.
PROBLEM: Null Pointer Exception at line 42 of your Render class
at Icon icon = this.field_94151_a.getIconFromDamage(this.field_94150_f);
SOLUTION: Be sure to finish initializing all Items before registering the Render class, otherwise the Item's icon will be null when the Render is registered, resulting in an NPE.
If you do make a new class, what does it extend? There's no CommonProxy class anywhere.
You need to make CommonProxy yourself:
public class CommonProxy implements IGuiHandler
{
/*
* Methods for ClientProxy to Override
*/
public void registerRenderers() {}
}
And in your main mod make an instance of it:
public final class YourMod
{
@Instance(ModInfo.ID)
public static YourMod instance = new YourMod();
@SidedProxy(clientSide = ModInfo.PROXY_LOCATION + ".client.ClientProxy", serverSide = ModInfo.PROXY_LOCATION + ".CommonProxy")
public static CommonProxy proxy;
// etc. then in load method:
@EventHandler
public void load(FMLInitializationEvent event)
{
proxy.registerRenderers();
// whatever else you need to register here as well
}
I have an error in the main mod class.
It is saying "ModInfo cannot be resolved to a variable".
Where and What is ModInfo meant to be?
Ah, oops. ModInfo is just a class I made so I don't have to worry about keeping my mod variables synchronized if I decide to change them. You can make one yourself like this:
public class ModInfo
{
public static final String ID = "yourmodid";
public static final String NAME = "Your Mod Name";
public static final String VERSION = "0.1.0";
public static final String CHANNEL = "modname";
public static final String PROXY_LOCATION = "yourname.modname"; // package where your main mod is located
}
Or you can just replace 'ModInfo.x' with the above values, so instead of 'ModInfo.ID' you'd put "yourmodid".
Some random tips to make your code look nicer (unless you prefer it your way, which isn't wrong by any means):
// Using an index allows you to adjust all of your Item indices just by changing this value. Useful for later mod compatability
private static final int modItemIndex = 3500;
// Do the same for blocks:
private static final int modBlockIndex = 500;
// You can finish declaring everything about your Item in this one line - no need to do half here and half in the load() method
// Instead of this:
public static final Item steel;
// Put this:
public static final Item steel = new IngotSteel(modItemIndex++);
// and your block:
public static final Block steelBlock = new SteelBlock(modBlockIndex++);
Oh, and there is no "@Init" anymore. These are the 3 methods used by Forge now:
@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
}
@EventHandler
public void load(FMLInitializationEvent event)
{
proxy.registerRenderers();
}
@EventHandler
public void postInit(FMLPostInitializationEvent event)
{
}
So remove the line "@Init" above your load method.
One last tip. You may know this already, but you only need to make a new class if you need new variables or want to change the way a certain method works. I'm betting most of your weapons work just like Swords, so you could probably get away with making your Items like this:
public static final Item woodenlongsword = new ItemSword(modItemIndex++, EnumToolMaterial.WOOD).setUnlocalizedName("woodenlongsword");
If you need your weapons to work differently than Swords in some way, you could make a new class ModWeapon that extends ItemSword, then just add / change what you want for each weapon. You probaly don't need a unique class for every single weapon. But then again, maybe all your weapons are very different from each other and need their own class to handle their unique characteristics and behavior. Just something to think about.
Great! That's progress, believe it or not You can get a null pointer exception in many ways, and they can be nasty to track down. Looking at your crash report, you see the null pointer exception is thrown on line 42 of doRender in RenderSnowball. That's this line:
So for some reason the Item NinjaStar that you passed in is null at this point. Without seeing the rest of your code, this is pure speculation, but in your NinjaStar class onItemUse method where you spawn the EntityNinjaStar into the world, try doing it like this:
// in the onItemUse() method:
EntityNinjaStar ninjastar = new EntityNinjaStar(your parameters here);
if (!this.worldObj.isRemote)
{
this.worldObj.spawnEntityInWorld(ninjastar);
}
See if that works. If not, post your Item NinjaStar and Entity NinjaStar classes here and I'll take a look. Sorry you're having so much trouble. When we get it working, you'll have to let me know how I can make the tutorial more clear
Hm, yeah your Item and Entity looked right the first time. In Item, you need to use the passed in par2World, not this.worldObj - sorry. It's really the same thing, but Item doesn't have a world object variable whereas an Entity does. Don't worry about it for now
I'll look at your other code again. Go ahead and change your Item back to what it was.
You know, it could very well be that you declare all of your Items AFTER you register the renderer in your main mod load() method.
Put all of your items like I suggested above after you declare the instance of your mod:
public final class YourMod
{
// The instance of your mod that Forge uses.
@Instance(ModInfo.ID)
public static YourMod instance;
// Says where the client and server 'proxy' code is loaded.
@SidedProxy(clientSide = ModInfo.PROXY_LOCATION + ".client.ClientProxy", serverSide = ModInfo.PROXY_LOCATION + ".CommonProxy")
public static CommonProxy proxy;
// All your Items and blocks here, but you MUST declare " = new Item(id) etc" here:
public static final Item ninjastar = new NinjaStar(3526);
// You can chain it if you want like this:
public static final Item ninjastar = new NinjaStar(3526).setUnlocalizedName("sharuken").setCreativeTab(CreativeTabs.tabMisc).setMaxStackSize(16);
// for most of your items (e.g. swords, spears, etc.) the above will be enough and you don't need a special class for them.
// for ninjastar, since you need it to do something special onRightClick, you need a special ItemNinjaStar class like you have
// If you prefer, you don't need to chain it and can set everything in the Item class, but you MUST at least declare the
// new Item (id) here
Since you are only instantiating the new item instance AFTER registering the renderer, it has nothing to work with, thus the null pointer.
might, but I believe it's if not absolutely necessary, at least standard practice to put all of the items where I mentioned. Try your way and see. If it doesn't work, try mine. Only way to tell for sure!
RenderingRegistry.registerEntityRenderingHandler(EntityRockSnowball.class, new RenderRockSnowball(ashtonsmod.RockSnowball));
error under ashtonsmod.RockSnowball
ashtonsmod.RockSnowball cannot be resolved to a type
but you said to refer to this :/
I believe you have confused the meaning of that section. Your custom class is called EntityRockSnowball or whatever, and you should be using "RenderSnowball" instead of a custom Render class like "RenderRockSnowball" until you can get it to render in game.
The reason you get an error with ashtonsmod.RockSnowball is probably because you forgot to make an Item called RockSnowball in your main mod.
Please read through the tutorial carefully, again, and be sure not to leave even a single line out.
For this tutorial, we're going to make a new item ThrowingRock and get it to render a custom texture when thrown. We'll assume you've got your main mod space set up. If not, I HIGHLY suggest you go over to TechGuy543's tutorial at http://www.minecraft...ding-tutorials/
and make sure you set up your main mod correctly or you will NOT be able to get your custom item rendering.
STEP 1: Creating a custom Item, e.g. ItemThrowingRock
Like I said, we're going to make a ThrowingRock. To do this, we just make a new class called ThrowingRock in one of our packages. This class needs to exend Item, but we're going to make it extend another class, BaseModItem, which extends Item. The magic of inheritance will allow our ThrowingRock to extend Item through BaseModItem. Here is BaseModItem's code:
So far our ThrowingRock doesn't do anything. We want this class to throw rocks, much like snowballs, so we'll steal ItemSnowball's onImpact function and paste it into our ItemThrowingRock class:
STEP 2: Creating a custom Entity, e.g. EntityThrowingRock
Ok, we want our ThrowingRock to behave similarly to snowballs, so let's look at that class.
Tweak 1: Do you see "byte b0" in the code? That's how much damage our entity will inflict. For snowballs, it is zero unless you hit a blaze. Well, we don't need that, so delete all that stuff about the blaze as well as the related import. Let's rename "byte b0" to "float rockDamage." Now it's obvious what it does. Now you have to change "(float)b0" to "rockDamage."
Tweak 2: Change the damage. Right now, it says "float rockDamage = 0"; let's change it to 2 so as not to make it too powerful, but you can change it to whatever you want.
Tweak 3: Our rock spawns snowballpoofs on impact. Lame. Just change "snowballpoof" to "crit" for now. It won't look awesome, but it's better than snow.
Here's what our new EntityThrowingRock code looks like, with renamed method parameters:
Note the "@Override" before our onImpact function - it's not critical here, as EntityThrowable.onImpact() doesn't have anything in it, but it's good practice to put this here; if the method name or signature ever changes in a Minecraft update, you will know immediately because your old method will give you an error. This means you need to find the new method signature and change your method name - simply removing @Override will get rid of the error, too, but then your method will never be called!
STEP 3: Registering your custom entity and renderer.
We've set up our custom Entity class, but Minecraft doesn't know about it yet. So we have to let it know in using EntityRegistry.registerModEntity in our main mod load method and tell it what renderer to use using RenderingRegistry.registerEntityRenderingHandler in our ClientProxy. Here's how:
First in your main mod class:
Now to the ClientProxy:
registerModEntity(...) takes the class, a name (just make it up here), a unique ID, the instance of YourMod, tracking range, frequency of tracking updates, and whether to send velocity information.
RenderSnowball(...) requires the item to be rendered as an argument; since we haven't declared any items locally, we use the declaration from the instance of our mod.
NOTE: See this thread http://www.minecraft...0#entry18822284 for an explanation of Mod Entity vs. Global Entity IDs.
NOTE: A good reference to decide what tracking update frequency to use: https://docs.google...output=html (from the post mentioned in the previous note). For projectiles, ranges of 10-20 are the norm.
NOTE: If you want your custom item to render differently from a vanilla item, you will need to make a custom render class. See the next step.
Your custom entity should now render correctly in the world! Congratulations!
STEP 4: Rendering: The Power of Inheritance
Okay, so you've got everything working correctly, but you want your Item to exhibit some custom behavior when it renders. We need to create a new Render class. The easiest way to do this is to copy from an existing Renderer and paste it into your new class. In our case, ThrowingRock behaves like Snowballs, so we will copy RenderSnowball and rename it to RenderThrowingRock:
Pure copy-paste. Genius. Be sure to import everything necessary. But now I'll let you in on an even BETTER way, that you should use whenever you can: make a new class that EXTENDS whatever class you want to emulate, rather than copying and pasting:
Wow, that's so much simpler. You barely have to do anything. Remember that for the future. Of course you don't really have to even create a Render class for the throwing rock, as you can just pass the Item directly to RenderSnowball when you register, but you can use this technique in many places, overriding methods to add new functionality while getting the benefits of the old.
to this:
Or if not using a custom render, use RenderSnowball with your Item as the parameter:
Now all you need to do is play around with the variables and see what you can get it to do.
STEP 5: Rendering a Model
If you want to do some different rendering than that offered by RenderSnowball, for example if you want a model, you will need to create a new class that extends Render, or one of Render's subclasses.
You will need a ResourceLocation for your texture; this takes 2 parameters: ModId and the path to your texture at the location: "src/minecraft/assets/modid/"
You can also set it with a single parameter, the string of the path to the texture:
"mymodid:textures/entity/mytexture.png" or (mymodid + ":textures/entity/mytexture.png").
In any class that extends Render, or TileEntitySpecialRenderer, you can bind the texture simply by using "this.bindTexture(yourResourceLocation);"
On to the class itself:
STEP 6: HELP!!! A.k.a Common Problems and Troubleshooting.
First, please read ALL sections of the above tutorial carefully. Then see if you have done all of the below:
1. Read ALL sections of the tutorial carefully.
2. Make sure you have created your texture using GIMP, Paint, whatever and saved in .png format.
3. Make sure your texture is in the correct folder; for forge, it is:
/forge/mcp/src/minecraft/assets/yourmodid/textures/items/
4. Make sure your folder "yourmodid" is all lower-case.
5. Re-read the tutorial. Did you follow ALL of the steps EXACTLY?
6. Did you double-check that your Main Mod, CommonProxy and ClientProxy are all set up properly?
Check TechGuy543's tutorial one more time to be sure.
Other problems will be noted below. I will update this section if anyone has further problems that aren't addressed elsewhere.
PROBLEM: Null Pointer Exception at line 42 of your Render class
at Icon icon = this.field_94151_a.getIconFromDamage(this.field_94150_f);
SOLUTION: Be sure to finish initializing all Items before registering the Render class, otherwise the Item's icon will be null when the Render is registered, resulting in an NPE.
Many thanks to those who've helped me and others, as well as those who have written other great tutorials. I recommend TechGuy543's tutorials http://www.minecraft...ding-tutorials/ as well as a great 3-part tutorial on the minecraftforge wiki http://www.minecraft...-_Blaster_Rifle.
Be sure to check out my other tutorials - they are all on github here or follow the forum links below:
- Custom Armors, the OOP Way (GitHub only)
- 1.7.2 Example Mod (GitHub only)
- Modding With APIs
- EventHandler and IExtendedEntityProperties
- Custom Inventories in Items and Players
- Multi-Input/Output Furnace
- Custom Projectile and Rendering
- Overriding shift-click in custom Containers
- Using Potions in Crafting Recipes
- Enchanted Books and Crafting Recipes
- See Mazetar's List for the best Minecraft tutorials from around the web!
Lastly, don't forget to up the green arrow on any posts that help you!
You need to make CommonProxy yourself:
And in your main mod make an instance of it:
Ah, oops. ModInfo is just a class I made so I don't have to worry about keeping my mod variables synchronized if I decide to change them. You can make one yourself like this:
Or you can just replace 'ModInfo.x' with the above values, so instead of 'ModInfo.ID' you'd put "yourmodid".
Also, doublecheck this: "@Instance("armourymod")" - replace "armourymod" with ModInfo.ID, just to be safe.
Try deleting these lines:
Some random tips to make your code look nicer (unless you prefer it your way, which isn't wrong by any means):
Oh, and there is no "@Init" anymore. These are the 3 methods used by Forge now:
So remove the line "@Init" above your load method.
If you need your weapons to work differently than Swords in some way, you could make a new class ModWeapon that extends ItemSword, then just add / change what you want for each weapon. You probaly don't need a unique class for every single weapon. But then again, maybe all your weapons are very different from each other and need their own class to handle their unique characteristics and behavior. Just something to think about.
Great! That's progress, believe it or not You can get a null pointer exception in many ways, and they can be nasty to track down. Looking at your crash report, you see the null pointer exception is thrown on line 42 of doRender in RenderSnowball. That's this line:
So for some reason the Item NinjaStar that you passed in is null at this point. Without seeing the rest of your code, this is pure speculation, but in your NinjaStar class onItemUse method where you spawn the EntityNinjaStar into the world, try doing it like this:
See if that works. If not, post your Item NinjaStar and Entity NinjaStar classes here and I'll take a look. Sorry you're having so much trouble. When we get it working, you'll have to let me know how I can make the tutorial more clear
And worldObj is the reference to the World that everything is spawned in etc. All entities have an associated world object.
I'll look at your other code again. Go ahead and change your Item back to what it was.
Put all of your items like I suggested above after you declare the instance of your mod:
Since you are only instantiating the new item instance AFTER registering the renderer, it has nothing to work with, thus the null pointer.
At least that's my guess
P.S. Don't forget to up the green arrow on posts that help you
I believe you have confused the meaning of that section. Your custom class is called EntityRockSnowball or whatever, and you should be using "RenderSnowball" instead of a custom Render class like "RenderRockSnowball" until you can get it to render in game.
The reason you get an error with ashtonsmod.RockSnowball is probably because you forgot to make an Item called RockSnowball in your main mod.
Please read through the tutorial carefully, again, and be sure not to leave even a single line out.
Yes, but it's not obvious because the method is not named nicely:
That code goes in your EntityThrowingRock, btw.
Thanks it works
Homework for you: look through EntityThrowable code and find a method that sets how gravity affects the trajectory.
When you find it, add it to your class and have it return 0.