From 12a56dfe79f363ac77130e81b7bbb375bb7fcc61 Mon Sep 17 00:00:00 2001 From: Sollace Date: Fri, 2 Oct 2020 11:55:59 +0200 Subject: [PATCH] Implement more realistic flying mechanics --- .../player/AeronauticalPlayerPhysics.java | 50 ---- .../unicopia/entity/player/Aeronautics.java | 73 ------ .../entity/player/PlayerDimensions.java | 14 +- .../unicopia/entity/player/PlayerPhysics.java | 220 ++++++++++-------- 4 files changed, 128 insertions(+), 229 deletions(-) delete mode 100644 src/main/java/com/minelittlepony/unicopia/entity/player/AeronauticalPlayerPhysics.java delete mode 100644 src/main/java/com/minelittlepony/unicopia/entity/player/Aeronautics.java diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/AeronauticalPlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/AeronauticalPlayerPhysics.java deleted file mode 100644 index 9db796ee..00000000 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/AeronauticalPlayerPhysics.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.minelittlepony.unicopia.entity.player; - -import com.minelittlepony.unicopia.util.MutableVector; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.math.Vec3d; - -public class AeronauticalPlayerPhysics extends PlayerPhysics { - - private final Aeronautics auronautics = new Aeronautics(); - - private double gravity; - - private int thrustCountdown; - - public AeronauticalPlayerPhysics(Pony pony) { - super(pony); - } - - @Override - public double calcGravity(double worldConstant) { - return gravity = super.calcGravity(worldConstant); - } - - @Override - protected void moveFlying(Entity player, MutableVector velocity) { - - PlayerEntity ply = (PlayerEntity)player; - - auronautics.pitchAngle = 0; - auronautics.rollAngle = 0; - - float yaw = player.getYaw(1); - - Vec3d motion = auronautics.calcGravitationalAccelleration(gravity); - - if (ply.forwardSpeed != 0 && thrustCountdown-- <= 0) { - thrustCountdown = 20; - player.playSound(getWingSound(), 0.4F, 1); - motion = motion.add(auronautics.calcThrustVelocity(-100)); - } - - motion = motion.rotateY(yaw).add(velocity.toImmutable()).multiply(1 - auronautics.getDrag()); - - velocity.x = motion.x; - velocity.y = motion.y; - velocity.z = motion.z; - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Aeronautics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Aeronautics.java deleted file mode 100644 index 8f37d15e..00000000 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Aeronautics.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.minelittlepony.unicopia.entity.player; - -import net.minecraft.util.math.Vec3d; - -// X - forward -// Y - vertical -// Z - sideways -public class Aeronautics { - - public double rollAngle; - public double pitchAngle; - - /** - * Gets the normalized direction vector - */ - private Vec3d getMomentVector() { - // sine(angle) = y/h - // cos(angle) = x/h - - Vec3d climbVector = new Vec3d(Math.cos(pitchAngle), Math.sin(pitchAngle), 0); - Vec3d bankVector = new Vec3d(0, Math.sin(rollAngle), Math.cos(rollAngle)); - - return bankVector.add(climbVector).normalize(); - } - - /** - * Gets the normalized perpendicular vector. - */ - private Vec3d getNormal() { - // sine(angle) = y/h - // cos(angle) = x/h - - Vec3d climbVector = new Vec3d(Math.cos(pitchAngle), Math.sin(pitchAngle), 0); - Vec3d bankVector = new Vec3d(0, Math.sin(rollAngle), Math.cos(rollAngle)); - - return bankVector.crossProduct(climbVector).normalize(); - } - - /** - * Returns the acceleration vector due to gravity - * parallel to the slope of the incline described - * by the roll and pitch components. - * - * @param gravity The global gravitation constant C - */ - public Vec3d calcGravitationalAccelleration(double gravity) { - return getMomentVector().multiply( - -gravity * Math.signum(pitchAngle), - -gravity, - -gravity * Math.signum(rollAngle) - ); - } - - /** - * Gets the added thrust vector for the given forwards motion - * and velocity projected against the direction of incline. - * - * @param forwards The forwards thrust speed - * @param velocity The current motion vector - */ - public Vec3d calcThrustVelocity(double forwards) { - return getNormal().add(getMomentVector()) - .normalize() - .multiply(forwards * (1 - getDrag())); - } - - /** - * The drag due to air resistance. - */ - public double getDrag() { - return 0.0078; // magic number until I figure out what tf I'm doing - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java index 2b289e09..781f4972 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java @@ -49,13 +49,9 @@ public final class PlayerDimensions { flyingDimensions = EntityDimensions.changing(dimensions.width, dimensions.height / 2); } - return getPredicate().flatMap(e -> e.getTargetDimensions(pony)).orElseGet(() -> { - if (physics.isFlyingSurvival && physics.isRainboom()) { - return flyingDimensions; - } - - return defaultDimensions; - }); + return getPredicate() + .flatMap(e -> e.getTargetDimensions(pony)) + .orElseGet(() -> physics.isFlyingSurvival ? flyingDimensions : defaultDimensions); } private float calculateTargetEyeHeight() { @@ -65,10 +61,6 @@ public final class PlayerDimensions { return height; } - if (physics.isFlyingSurvival && physics.isRainboom()) { - return 0.5F; - } - return defaultEyeHeight; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index 180a3e34..7955c59f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -15,20 +15,21 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; -import net.minecraft.sound.SoundEvents; import net.minecraft.util.Tickable; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; public class PlayerPhysics extends EntityPhysics implements Tickable, Motion, NbtSerialisable { private int ticksInAir; + private float thrustScale = 0; + public boolean isFlyingEither = false; public boolean isFlyingSurvival = false; - private double lastTickPosX = 0; - private double lastTickPosZ = 0; + private Vec3d lastPos = Vec3d.ZERO; private final PlayerDimensions dimensions; @@ -47,30 +48,16 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti return super.getGravityModifier() * (float)pony.getOwner().getAttributeValue(PlayerAttributes.ENTITY_GRAVTY_MODIFIER); } - private boolean checkCanFly() { - if (pony.getOwner().abilities.creativeMode || pony.getOwner().isSpectator()) { - return true; - } - - if (pony.hasSpell()) { - Spell effect = pony.getSpell(true); - if (!effect.isDead() && effect instanceof FlightPredicate) { - return ((FlightPredicate)effect).checkCanFly(pony); - } - } - - return pony.getSpecies().canFly(); - } - - protected boolean isRainboom() { - return Math.sqrt(getHorizontalMotion(pony.getOwner())) > 0.4F; - } - @Override public PlayerDimensions getDimensions() { return dimensions; } + @Override + public boolean isFlying() { + return isFlyingSurvival && !pony.getOwner().isFallFlying() && !pony.getOwner().hasVehicle(); + } + @Override public void tick() { PlayerEntity entity = pony.getOwner(); @@ -83,32 +70,37 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti entity.setPose(EntityPose.STANDING); } - MutableVector velocity = new MutableVector(entity.getVelocity()); + final MutableVector velocity = new MutableVector(entity.getVelocity()); boolean creative = entity.abilities.creativeMode || pony.getOwner().isSpectator(); - entity.abilities.allowFlying = checkCanFly(); + boolean canFly = checkCanFly(); if (!creative) { - entity.abilities.flying |= entity.abilities.allowFlying && isFlyingEither; + entity.abilities.flying |= (canFly || entity.abilities.allowFlying) && isFlyingEither; - if ((entity.isOnGround() && entity.isSneaking()) || entity.isTouchingWater()) { + if ((entity.isOnGround() && entity.isSneaking()) || entity.isTouchingWater() || entity.horizontalCollision || entity.verticalCollision) { entity.abilities.flying = false; } - - } isFlyingSurvival = entity.abilities.flying && !creative; isFlyingEither = isFlyingSurvival || (creative && entity.abilities.flying); - if (!creative && !entity.isFallFlying() && isFlyingSurvival && !entity.hasVehicle()) { + if (pony.getPhysics().isGravityNegative()) { + entity.setOnGround(!entity.world.isAir(new BlockPos(entity.getX(), entity.getY() + entity.getHeight() + 0.5F, entity.getZ()))); - entity.fallDistance = 0; + if (entity.isOnGround() || entity.horizontalCollision) { + entity.abilities.flying = false; + isFlyingEither = false; + isFlyingSurvival = false; + } + } + if (isFlying()) { int level = pony.getLevel().get() + 1; - if (ticksInAir > (level * 100)) { + if (ticksInAir++ > (level * 100)) { Bar mana = pony.getMagicalReserves().getMana(); mana.add((int)(-getHorizontalMotion(entity) * 50 / level)); @@ -128,100 +120,143 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti } } - moveFlying(entity, velocity); + entity.fallDistance = 0; - if (ticksInAir++ > 0 && ticksInAir % 30 == 0) { - entity.playSound(getWingSound(), 0.5F, 1); + moveFlying(entity, velocity); + if (entity.world.hasRain(entity.getBlockPos())) { + applyTurbulance(entity, velocity); } + + if (entity.world.isClient && ticksInAir % 20 == 0 && entity.getVelocity().length() < 0.29) { + entity.playSound(getWingSound(), 0.5F, 1); + thrustScale = 1; + } + velocity.y -= 0.02; } else { ticksInAir = 0; - } - if (pony.getPhysics().isGravityNegative()) { - entity.setOnGround(!entity.world.isAir(new BlockPos(entity.getX(), entity.getY() + entity.getHeight() + 0.5F, entity.getZ()))); + if (!creative) { - if (entity.isOnGround()) { - entity.abilities.flying = false; - isFlyingSurvival = false; + double horMotion = getHorizontalMotion(entity); + double motion = entity.getPos().subtract(lastPos).lengthSquared(); + + if (velocity.y > 0 && (horMotion > 0.2 || (motion > 0.2 && velocity.y < -0.2))) { + entity.abilities.flying = true; + isFlyingEither = true; + isFlyingSurvival = true; + + velocity.y += horMotion + 0.3; + applyThrust(entity, velocity); + } } } - lastTickPosX = entity.getX(); - lastTickPosZ = entity.getZ(); + lastPos = new Vec3d(entity.getX(), 0, entity.getZ()); entity.setVelocity(velocity.toImmutable()); } - public SoundEvent getWingSound() { + private SoundEvent getWingSound() { return pony.getSpecies() == Race.CHANGELING ? USounds.CHANGELING_BUZZ : USounds.WING_FLAP; } - protected void moveFlying(Entity player, MutableVector velocity) { + protected void moveFlying(PlayerEntity player, MutableVector velocity) { + applyThrust(player, velocity); - float forward = 0.000015F * (1 + (pony.getLevel().get() / 10F)) * (float)Math.sqrt(getHorizontalMotion(player)); - boolean sneak = !player.isSneaking(); + double motion = getHorizontalMotion(player); + + float forward = 0.000015F * (1 + (pony.getLevel().get() / 10F)) * (float)Math.sqrt(motion); // vertical drop due to gravity - if (sneak) { - velocity.y -= (0.005F - getHorizontalMotion(player) / 100) * getGravitySignum(); - } else { - forward += 0.005F; - velocity.y -= 0.0001F * getGravitySignum(); - } + forward += 0.005F; + + velocity.y -= (getGravityModifier() * 0.01F) / Math.max(motion * 100, 1); velocity.x += - forward * MathHelper.sin(player.yaw * 0.017453292F); velocity.z += forward * MathHelper.cos(player.yaw * 0.017453292F); + } - if (player.world.hasRain(player.getBlockPos())) { - float glance = 360 * player.world.random.nextFloat(); - - - forward = 0.015F * player.world.random.nextFloat() * player.world.getRainGradient(1); - - if (player.world.random.nextInt(30) == 0) { - forward *= 10; - } - if (player.world.random.nextInt(30) == 0) { - forward *= 10; - } - if (player.world.random.nextInt(40) == 0) { - forward *= 100; - } - - if (player.world.isThundering() && player.world.random.nextInt(60) == 0) { - velocity.y += forward * 3; - } - - if (forward >= 1) { - player.world.playSound(null, player.getBlockPos(), USounds.WIND_RUSH, SoundCategory.AMBIENT, 3, 1); - } - - if (forward > 4) { - forward = 4; - } - - velocity.x += - forward * MathHelper.sin((player.yaw + glance) * 0.017453292F); - velocity.z += forward * MathHelper.cos((player.yaw + glance) * 0.017453292F); + protected void applyThrust(PlayerEntity player, MutableVector velocity) { + if (pony.sneakingChanged() && player.isSneaking()) { + thrustScale = 1; + player.playSound(getWingSound(), 0.5F, 1); + } else { + thrustScale *= 0.1889F; } + + float thrustStrength = 0.135F * thrustScale; + Vec3d direction = player.getRotationVec(1).normalize().multiply(thrustStrength); + + velocity.x += direction.x; + velocity.z += direction.z; + velocity.y += direction.y * 2.45 + Math.abs(direction.y) * 10; + + if (player.isSneaking()) { + velocity.y += 0.4 - 0.25; + if (pony.sneakingChanged()) { + velocity.y += 0.75; + } + } else { + velocity.y -= 0.1; + } + + } + + protected void applyTurbulance(Entity player, MutableVector velocity) { + float glance = 360 * player.world.random.nextFloat(); + float forward = 0.015F * player.world.random.nextFloat() * player.world.getRainGradient(1); + + if (player.world.random.nextInt(30) == 0) { + forward *= 10; + } + if (player.world.random.nextInt(30) == 0) { + forward *= 10; + } + if (player.world.random.nextInt(40) == 0) { + forward *= 100; + } + + if (player.world.isThundering() && player.world.random.nextInt(60) == 0) { + velocity.y += forward * 3; + } + + if (forward >= 1) { + player.world.playSound(null, player.getBlockPos(), USounds.WIND_RUSH, SoundCategory.AMBIENT, 3, 1); + } + + if (forward > 4) { + forward = 4; + } + + velocity.x += - forward * MathHelper.sin((player.yaw + glance) * 0.017453292F); + velocity.z += forward * MathHelper.cos((player.yaw + glance) * 0.017453292F); } protected double getHorizontalMotion(Entity e) { - double motionX = e.getX() - lastTickPosX; - double motionZ = e.getZ() - lastTickPosZ; - - return (motionX * motionX) + (motionZ * motionZ); + return Entity.squaredHorizontalLength(e.getPos().subtract(lastPos)); } - protected SoundEvent getFallSound(int distance) { - return distance > 4 ? SoundEvents.ENTITY_PLAYER_BIG_FALL : SoundEvents.ENTITY_PLAYER_SMALL_FALL; + private boolean checkCanFly() { + if (pony.getOwner().abilities.creativeMode || pony.getOwner().isSpectator()) { + return true; + } + + if (pony.hasSpell()) { + Spell effect = pony.getSpell(true); + if (!effect.isDead() && effect instanceof FlightPredicate) { + return ((FlightPredicate)effect).checkCanFly(pony); + } + } + + return pony.getSpecies().canFly(); } public void updateFlightStat(boolean flying) { PlayerEntity entity = pony.getOwner(); - entity.abilities.allowFlying = checkCanFly(); + boolean canFly = checkCanFly(); - if (entity.abilities.allowFlying) { + if (canFly || entity.abilities.allowFlying) { entity.abilities.flying |= flying; isFlyingSurvival = entity.abilities.flying; @@ -248,9 +283,4 @@ public class PlayerPhysics extends EntityPhysics implements Tickable, Moti pony.getOwner().calculateDimensions(); } - - @Override - public boolean isFlying() { - return isFlyingSurvival; - } }