Jump to content

Recommended Posts

Posted

Here is my hot tub. As you can see, I can hop inside of it. It has walls which work properly so that I can't just walk out of it.

xiFvECV.png

But here's the major problem. When I make the hot tub larger, it doesn't get rid of the collision box for the wall that is no longer there. So because of that, I can't move around in the hot tub (move to the other hot tub part that I placed down). It's only meant to have collision boxes on the faces that are false.

 

Just realised my problem but don't know how to fix it. The addCollisionBoxes method only gets called on once - when it's first placed down. So when the blockstate properties change, the collision boxes don't get updated. How do I update the collision boxes?

 

Block Class:

 

public class BlockHotTub extends Block {

 

public static final PropertyBool BACK = PropertyBool.create("back");

public static final PropertyBool FORWARD = PropertyBool.create("forward");

public static final PropertyBool LEFT = PropertyBool.create("left");

public static final PropertyBool RIGHT = PropertyBool.create("right");

 

public BlockHotTub(Material materialIn) {

super(materialIn);

this.setCreativeTab(OddmentsMod.tabOddments);

this.setDefaultState(this.blockState.getBaseState().withProperty(BACK, false).withProperty(FORWARD, false).withProperty(LEFT, false).withProperty(RIGHT, false));

}

 

@Override

public boolean isOpaqueCube() {

return false;

}

 

@Override

public boolean isFullCube() {

return false;

}

 

@Override

public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {

boolean forward = world.getBlockState(pos.north()).getBlock() == this;

boolean back = world.getBlockState(pos.south()).getBlock() == this;

boolean left = world.getBlockState(pos.west()).getBlock() == this;

boolean right = world.getBlockState(pos.east()).getBlock() == this;

return state.withProperty(BACK, back).withProperty(FORWARD, forward).withProperty(LEFT, left).withProperty(RIGHT, right);

}

 

@Override

protected BlockState createBlockState() {

return new BlockState(this, new IProperty[] {BACK, FORWARD, LEFT, RIGHT});

}

 

@Override

public int getMetaFromState(IBlockState state) {

return  0;

}

 

@Override

public IBlockState getStateFromMeta(int meta) {

return this.getDefaultState();

}

 

// Not working but this method is meant to update the value of the boolean properties whenever a hot tub is connected to the current one

@Override

public void onNeighborBlockChange(World worldIn, BlockPos pos, IBlockState state, Block neighborBlock) {

getActualState(state, worldIn, pos);

addCollisionBoxesToList(worldIn, pos, state, null, null, null);

    }

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {

if (state.getValue(BACK) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if (state.getValue(FORWARD) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if (state.getValue(LEFT) == false) {

this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

if  (state.getValue(RIGHT) == false) {

this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

}

this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?

super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);

    }

}

 

Posted

The state being passed in is the state you should compute the collision boxes for. So you need to analyze the state and then return different collision boxes based on that.

 

Great, so I just need to check the value of the state passed through using an if statement in this method and add the collision box accordingly?

Posted

Sorry, I thought I understood what to do but apparently not. I have one blockstate for the hot tub and in there I have this:

{
    "variants": {
        "normal": {"model": "odm:hot_tub_empty"},
        "back=false,forward=false,left=false,right=false": {"model": "odm:hot_tub_empty"},
        "back=true,forward=true,left=true,right=true": {"model": "odm:hot_tub_center_empty"},
        "back=true,forward=false,left=false,right=false": {"model": "odm:hot_tub_side_empty"},
        "back=false,forward=true,left=false,right=false": {"model": "odm:hot_tub_side_empty", "y": 180},
        "back=false,forward=false,left=true,right=false": {"model": "odm:hot_tub_side_empty", "y": 270},
        "back=false,forward=false,left=false,right=true": {"model": "odm:hot_tub_side_empty", "y": 90},
        "back=true,forward=true,left=false,right=false": {"model": "odm:hot_tub_sides_two_empty", "y": 90},
        "back=false,forward=false,left=true,right=true": {"model": "odm:hot_tub_sides_two_empty"},
        "back=true,forward=false,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 270},
        "back=true,forward=false,left=false,right=true": {"model": "odm:hot_tub_corner_empty"},
        "back=false,forward=true,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 180},
        "back=true,forward=false,left=true,right=false": {"model": "odm:hot_tub_corner_empty", "y": 270},
        "back=true,forward=true,left=false,right=true": {"model": "odm:hot_tub_sides_three_empty"},
        "back=false,forward=true,left=true,right=true": {"model": "odm:hot_tub_sides_three_empty", "y": 90},
        "back=true,forward=true,left=true,right=false": {"model": "odm:hot_tub_sides_three_empty", "y": 190},
        "back=true,forward=false,left=true,right=true": {"model": "odm:hot_tub_sides_three_empty", "y": 270},
    }
}

 

And in the hot tub's class I have set the blockstate properties:

public static final PropertyBool BACK = PropertyBool.create("back");
public static final PropertyBool FORWARD = PropertyBool.create("forward");
public static final PropertyBool LEFT = PropertyBool.create("left");
public static final PropertyBool RIGHT = PropertyBool.create("right");

 

So under the addCollisionBoxesToList method, what exactly should I be checking for the value of in the if statements?

Posted

You get the values for those properties out of the block state using

IBlockState::getValue

.

 

Sorry again but I'm still struggling to get this to work.

 

I'm checking the value of one of the properties to see if it is true, then if it is I alter the collision box. But when I test this in-game, the block just doesn't have a collision box at all (no matter whether the back is true or false).

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == true) {
		this.setBlockBounds(0.5F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
        super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
    }

Posted

Don't call setBlockBounds like that, you have to actually add the collision box.

 

Do you mean by calling setBlockBounds in the constructor? Because when I do that, I still get this problem where my collision boxes aren't all adding together but instead it's just taking the last block bounds that I set in the addCollisionBoxes method. 

 

I want the hot tub to have a regular 1x1x1 bound box which you look at to select the block and break/interact with it. But I want the actual collision of the hot tub to vary depending on what type of hot tub it is (if it's facing left it's missing its left wall or if it's in the corner it's missing both its right and left wall etc.)

I have looked at the BrewingStand code as Choonster pointed out to be a good reference for collision boxes, but I can't find anything in there which uses some other function to add the collision box.

Posted

Which version of Minecraft are you using? My advice in your previous thread assumed you were using 1.9+, but it looks like you're using 1.8.9 (where the collision box logic is slightly different).

 

In 1.8.x, you override

Block#addCollisionBoxesToList

to call

Block#setBlockBounds

followed by the super method for each bounding box, then call

Block#setBlockBounds

to restore the block's default bounding box at the end of the method.

 

To make it easier to update to newer versions, I suggest you store each bounding box in a

static final

field and create a method like

Block#setBlockBounds

that sets

Block#minX/Y/Z

and

Block#maxX/Y/Z

from the coordinates of an

AxisAlignedBoundingBox

. When you update to 1.9+, you can simply replace each pair of calls to this method and the super method with calls to

Block.addCollisionBoxToList

.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Which version of Minecraft are you using? My advice in your previous thread assumed you were using 1.9+, but it looks like you're using 1.8.9 (where the collision box logic is slightly different).

 

In 1.8.x, you override

Block#addCollisionBoxesToList

to call

Block#setBlockBounds

followed by the super method for each bounding box, then call

Block#setBlockBounds

to restore the block's default bounding box at the end of the method.

 

You are correct, I am using 1.8.9. So here's my current code (I added that last thing you said to it):

 

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F); // the default bounding box at the end of the method?
                super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }

 

The default bounding box is the base of the hot tub which is obviously always going to be there regardless of how big the hot tub is.

Posted

Are those properties stored in the metadata or set in the actual state? If it's the latter, you have to call

Block#getActualState

yourself.

 

In 1.11.2, an extra

boolean

parameter was added to

Block#addCollisionBoxToList

indicating whether the actual state should be used (

false

) or not (

true

).

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Are those properties stored in the metadata or set in the actual state? If it's the latter, you have to call

Block#getActualState

yourself.

 

In 1.11.2, an extra

boolean

parameter was added to

Block#addCollisionBoxToList

indicating whether the actual state should be used (

false

) or not (

true

).

 

I believe they are stored in the actual state. But to avoid any confusion, here is the hot tub's class code just in case I'm wrong:

public class BlockHotTub extends Block {

public static final PropertyBool BACK = PropertyBool.create("back");
public static final PropertyBool FORWARD = PropertyBool.create("forward");
public static final PropertyBool LEFT = PropertyBool.create("left");
public static final PropertyBool RIGHT = PropertyBool.create("right");

public BlockHotTub(Material materialIn) {
	super(materialIn);
	this.setCreativeTab(OddmentsMod.tabOddments);
	this.setDefaultState(this.blockState.getBaseState().withProperty(BACK, false).withProperty(FORWARD, false).withProperty(LEFT, false).withProperty(RIGHT, false));
}

@Override
public boolean isOpaqueCube() {
	return false;
}

@Override
public boolean isFullCube() {
	return false;
}

@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
	boolean forward = world.getBlockState(pos.north()).getBlock() == this;
	boolean back = world.getBlockState(pos.south()).getBlock() == this;
	boolean left = world.getBlockState(pos.west()).getBlock() == this;
	boolean right = world.getBlockState(pos.east()).getBlock() == this;
	return state.withProperty(BACK, back).withProperty(FORWARD, forward).withProperty(LEFT, left).withProperty(RIGHT, right);
}

@Override
protected BlockState createBlockState() {
	return new BlockState(this, new IProperty[] {BACK, FORWARD, LEFT, RIGHT});
}

@Override
public int getMetaFromState(IBlockState state) {
	return  0;
}

@Override
public IBlockState getStateFromMeta(int meta) {
	return this.getDefaultState();
}

// Not working but this method is meant to update the value of the boolean properties whenever a hot tub is connected to the current one
@Override
public void onNeighborBlockChange(World worldIn, BlockPos pos, IBlockState state, Block neighborBlock) {
	getActualState(state, worldIn, pos);
    }

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }
}

 

Just realised my problem but don't know how to fix it. The addCollisionBoxes method only gets called on once - when it's first placed down. So when the blockstate properties change, the collision boxes don't get updated. How do I update the collision boxes?

Posted

Since the properties are stored in the actual state, you need to call

Block#getActualState

and get the values from the

IBlockState

returned by it.

 

Block#addCollisionBoxesToList

is called whenever an entity moves into the block space and in various other places. You don't need to manually update it.

 

Side note: Always annotate override methods with

@Override

.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Since the properties are stored in the actual state, you need to call

Block#getActualState

and get the values from the

IBlockState

returned by it.

 

Block#addCollisionBoxesToList

is called whenever an entity moves into the block space and in various other places. You don't need to manually update it.

 

Side note: Always annotate override methods with

@Override

.

 

Okay, I'm slightly confused as to how to call it. Should I just be calling getActualState within the addCollisionBoxes method like so?

@Override
public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	getActualState(state, worldIn, pos);
	if (state.getValue(BACK) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 0.125F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(FORWARD) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.875F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if (state.getValue(LEFT) == false) {
		this.setBlockBounds(0.0F, 0.0F, 0.0F, 0.125F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	if  (state.getValue(RIGHT) == false) {
		this.setBlockBounds(0.875F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
	}
	this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.1875F, 1.0F); // the default bounding box at the end of the method?
	super.addCollisionBoxesToList(worldIn, pos, state, mask, list, collidingEntity);
    }

Posted

getActualState

doesn't modify its arguments (and it can't, since

IBlockState

is immutable), it returns the actual state.

 

You need to get the values from the

IBlockState

returned by

getActualState

instead of getting them from the

IBlockState

argument.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

You need to get the values from the

IBlockState

returned by

getActualState

 

I can tell you're spelling the answer out for me but I'm just not getting it :-\

 

The addCollisionBoxesToList method needs to constantly know what BACK, FORWARD, LEFT and RIGHT are equal to. And to do this, you're saying that I need to call the getActualState method whenever addCollisionBoxesToList is called on.

 

This makes complete sense to me so far, but the matter of calling the method properly is what I don't understand.

 

This is my understanding of it:

The getActualState method sets the boolean fields: forward, back, left and right and then gives those values to my PropertyBools using the return keyword followed by state.withProperty(BACK, back), state.withProperty(FORWARD, forward), state.withProperty(LEFT, left) and state.withProperty(RIGHT, right). When the addCollisionBoxesToList method checks what, for example, BACK is equal to, shouldn't I call everything in the getActualState method?

Posted

Your

getActualState

method checks each of the adjacent blocks to see if they're hot tubs and then returns an

IBlockState

with the

BACK

,

FORWARD

,

LEFT

and

RIGHT

properties set to the appropriate values.

 

Your

addCollisionBoxesToList

method needs to call

getActualState

and store the

IBlockState

returned by it in a variable. It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Posted

Your

getActualState

method checks each of the adjacent blocks to see if they're hot tubs and then returns an

IBlockState

with the

BACK

,

FORWARD

,

LEFT

and

RIGHT

properties set to the appropriate values.

 

I completely understand this and it serves as a much better explanation of the code than my attempt. 

 

Your

addCollisionBoxesToList

method needs to call

getActualState

Got it.

public void addCollisionBoxesToList(World worldIn, BlockPos pos, IBlockState state, AxisAlignedBB mask, List<AxisAlignedBB> list, Entity collidingEntity) {
	getActualState(state, worldIn, pos);

 

and store the

IBlockState

returned by it in a variable.

This is where you lose me. What exactly is it that I'm storing in this variable? And are you referring to each of the four possible properties? Are these what I need to be storing in variables?

 

It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

So here you mean instead of having state.getValue(BACK) I should be putting state.getValue(variable that stores the state.withProperty(BACK, back)?

 

It's 3AM and I should probably spare you anymore trouble. I feel like a pest at this point. Maybe going over the forge documentation a few more times and I'll eventually get it :'(

 

 

Posted

and store the

IBlockState

returned by it in a variable.

This is where you lose me. What exactly is it that I'm storing in this variable? And are you referring to each of the four possible properties? Are these what I need to be storing in variables?

 

getActualState

returns a value of type

IBlockState

, you need to store this value in a variable.

 

 

 

It then needs to call

IBlockState#getValue

on this

IBlockState

value instead of the

IBlockState

argument.

So here you mean instead of having state.getValue(BACK) I should be putting state.getValue(variable that stores the state.withProperty(BACK, back)?

 

Instead of calling

state.getValue(...)

, call

actualState.getValue(...)

; where

actualState

is the variable storing the value returned by

getActualState

.

  • Like 1

Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.