mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-03-04 06:44:40 +01:00
Sound engine running full steam
- Play at most 4 steam engine sounds at once per side of a boiler - 3 seems to be too few for this case as there's still a perceptible difference between 3 and 4 sounds per side - Track which positions want to play the sound in any given rolling window of ticks, and roll dice to decide which positions actually get to play - Introduce SoundPool to track that and explain the perception stuffs
This commit is contained in:
parent
628d1cfba9
commit
cb19df09c1
3 changed files with 130 additions and 8 deletions
|
@ -1,6 +1,7 @@
|
|||
package com.simibubi.create.content.fluids.tank;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -8,6 +9,7 @@ import java.util.Set;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.Create;
|
||||
import com.simibubi.create.content.decoration.steamWhistle.WhistleBlock;
|
||||
import com.simibubi.create.content.decoration.steamWhistle.WhistleBlockEntity;
|
||||
|
@ -29,6 +31,8 @@ import net.minecraft.core.Direction;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
@ -65,10 +69,25 @@ public class BoilerData {
|
|||
|
||||
public LerpedFloat gauge = LerpedFloat.linear();
|
||||
|
||||
// client only sound control
|
||||
|
||||
// re-use the same lambda for each side
|
||||
private final SoundPool.Sound sound = (level, pos) -> {
|
||||
float volume = 3f / Math.max(2, attachedEngines / 6);
|
||||
float pitch = 1.18f - level.random.nextFloat() * .25f;
|
||||
level.playLocalSound(pos.getX(), pos.getY(), pos.getZ(),
|
||||
SoundEvents.CANDLE_EXTINGUISH, SoundSource.BLOCKS, volume, pitch, false);
|
||||
|
||||
AllSoundEvents.STEAM.playAt(level, pos, volume / 16, .8f, false);
|
||||
};
|
||||
// separate pools for each side so they sound distinct when standing at corners of the boiler
|
||||
private final EnumMap<Direction, SoundPool> pools = new EnumMap<>(Direction.class);
|
||||
|
||||
public void tick(FluidTankBlockEntity controller) {
|
||||
if (!isActive())
|
||||
return;
|
||||
if (controller.getLevel().isClientSide) {
|
||||
pools.values().forEach(p -> p.play(controller.getLevel()));
|
||||
gauge.tickChaser();
|
||||
float current = gauge.getValue(1);
|
||||
if (current > 1 && Create.RANDOM.nextFloat() < 1 / 2f)
|
||||
|
@ -105,6 +124,15 @@ public class BoilerData {
|
|||
controller.notifyUpdate();
|
||||
}
|
||||
|
||||
public void queueSoundOnSide(BlockPos pos, Direction side) {
|
||||
SoundPool pool = pools.get(side);
|
||||
if (pool == null) {
|
||||
pool = new SoundPool(4, 2, sound);
|
||||
pools.put(side, pool);
|
||||
}
|
||||
pool.queueAt(pos);
|
||||
}
|
||||
|
||||
public int getTheoreticalHeatLevel() {
|
||||
return activeHeat;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package com.simibubi.create.content.fluids.tank;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
/**
|
||||
* One person walking sounds like one person walking, and you can easily distinguish where they are.
|
||||
*
|
||||
* <br>With two people walking, you can still pick out which footsteps belong to which person.
|
||||
*
|
||||
* <br>Try and listen to three people walking in a group, however, and you'll find that you can't distinguish
|
||||
* individual footsteps anymore. You now just hear the sound of a group of people walking.
|
||||
*
|
||||
* <p>You'll likely find that you perceive any number of people walking in a group as a single distinguishable sound.
|
||||
* This class is a helper to take advantage of that for sound effects in Create to avoid saturating the sound engine
|
||||
* without a perceptible loss in quality.
|
||||
*
|
||||
* <p>NOTE: It's up to the user of this class to decide how to group sounds such that they are perceived as a single
|
||||
* sound. There are no spatial calculations made here.
|
||||
*/
|
||||
public class SoundPool {
|
||||
/**
|
||||
* The maximum number of sounds that can be played at once.
|
||||
*/
|
||||
private final int maxConcurrent;
|
||||
/**
|
||||
* The number of ticks to wait before playing sounds. Useful if sounds are queued across many block entities,
|
||||
* and you don't have control over the tick order.
|
||||
*/
|
||||
private final int mergeTicks;
|
||||
|
||||
private final Sound sound;
|
||||
|
||||
private final LongList queuedPositions = new LongArrayList();
|
||||
|
||||
private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
|
||||
private int ticks = 0;
|
||||
|
||||
public SoundPool(int maxConcurrent, int mergeTicks, Sound sound) {
|
||||
this.maxConcurrent = maxConcurrent;
|
||||
this.sound = sound;
|
||||
this.mergeTicks = mergeTicks;
|
||||
}
|
||||
|
||||
public void queueAt(BlockPos pos) {
|
||||
queueAt(pos.asLong());
|
||||
}
|
||||
|
||||
public void queueAt(long pos) {
|
||||
queuedPositions.add(pos);
|
||||
}
|
||||
|
||||
public void play(Level level) {
|
||||
if (queuedPositions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ticks++;
|
||||
|
||||
if (ticks < mergeTicks) {
|
||||
// Wait for more sounds to be queued in further ticks.
|
||||
return;
|
||||
}
|
||||
|
||||
ticks = 0;
|
||||
|
||||
var numberOfPositions = queuedPositions.size();
|
||||
|
||||
if (numberOfPositions <= maxConcurrent) {
|
||||
// Fewer sound positions than maxConcurrent, play them all.
|
||||
for (long pos : queuedPositions) {
|
||||
playAt(level, pos);
|
||||
}
|
||||
} else {
|
||||
// Roll for n random positions and play there.
|
||||
while (!queuedPositions.isEmpty() && queuedPositions.size() > numberOfPositions - maxConcurrent) {
|
||||
rollNextPosition(level);
|
||||
}
|
||||
}
|
||||
|
||||
queuedPositions.clear();
|
||||
}
|
||||
|
||||
private void rollNextPosition(Level level) {
|
||||
int index = level.random.nextInt(queuedPositions.size());
|
||||
long pos = queuedPositions.removeLong(index);
|
||||
playAt(level, pos);
|
||||
}
|
||||
|
||||
private void playAt(Level level, long pos) {
|
||||
sound.playAt(level, this.pos.set(pos));
|
||||
}
|
||||
|
||||
public interface Sound {
|
||||
void playAt(Level level, Vec3i pos);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import java.util.List;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
import com.simibubi.create.AllBlocks;
|
||||
import com.simibubi.create.AllSoundEvents;
|
||||
import com.simibubi.create.content.contraptions.bearing.WindmillBearingBlockEntity.RotationDirection;
|
||||
import com.simibubi.create.content.equipment.goggles.IHaveGoggleInformation;
|
||||
import com.simibubi.create.content.fluids.tank.FluidTankBlockEntity;
|
||||
|
@ -26,8 +25,6 @@ import net.minecraft.core.Direction;
|
|||
import net.minecraft.core.Direction.Axis;
|
||||
import net.minecraft.core.Direction.AxisDirection;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
|
@ -208,11 +205,7 @@ public class SteamEngineBlockEntity extends SmartBlockEntity implements IHaveGog
|
|||
if (sourceBE != null) {
|
||||
FluidTankBlockEntity controller = sourceBE.getControllerBE();
|
||||
if (controller != null && controller.boiler != null) {
|
||||
float volume = 3f / Math.max(2, controller.boiler.attachedEngines / 6);
|
||||
float pitch = 1.18f - level.random.nextFloat() * .25f;
|
||||
level.playLocalSound(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(),
|
||||
SoundEvents.CANDLE_EXTINGUISH, SoundSource.BLOCKS, volume, pitch, false);
|
||||
AllSoundEvents.STEAM.playAt(level, worldPosition, volume / 16, .8f, false);
|
||||
controller.boiler.queueSoundOnSide(worldPosition, SteamEngineBlock.getFacing(getBlockState()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue