这是我的代码:
@SuppressWarnings("deprecation")
@EventHandler
public void onPlayerInteractBlockSummonElemental(PlayerInteractEvent event) {
final Player player = event.getPlayer();
if (player.getFoodLevel() > 10){
if (player.getItemInHand().getType() == Material.BLAZE_POWDER){
List<Block> targets = player.getLineOfSight((Set)null, 100);
final Creature blaze1 = (Creature) player.getWorld().spawnCreature(player.getLocation(), EntityType.BLAZE);
final Creature blaze2 = (Creature) player.getWorld().spawnCreature(player.getLocation(), EntityType.BLAZE);
for (Block target : targets){
player.playEffect(target.getLocation(), Effect.SMOKE,5);
List<Entity> victims = (List<Entity>) target.getWorld().getNearbyEntities(target.getLocation(), 2, 2, 2);
for (Entity victim2 : victims){
final LivingEntity victim = (LivingEntity) victim2;
if (victim != blaze1){
if (victim != blaze2){
if (victim != player){
blaze1.setTarget(victim);
blaze2.setTarget(victim);
int count = 0;
while (count < 6) {
Bukkit.getServer().getScheduler().runTaskLater(MagictgCraft.that, new Runnable(){
public void run(){
blaze1.setTarget(victim);
blaze2.setTarget(victim);
if (blaze1.getTarget() == player){
blaze1.damage(20);
}
if (blaze2.getTarget() == player){
blaze2.damage(20);
}
}
},50);
count = count + 1;
}
blaze1.damage(20);
blaze2.damage(20);
}
}
}
}
}
int manaused = player.getFoodLevel();
manaused = manaused - 10;
player.setFoodLevel(manaused);
}
}
我正在使用计时器来检查火焰是否会攻击施放它们的玩家,并重置目标。我不知道为什么,但我的循环中没有任何内容发生。有时候,火焰会立即死亡,有时候,火焰会产生火焰而且会产生火焰,有时它会产生火焰并抛出错误,说“无法将事件PlayerInteractEvent传递给插件v1.0&#39;”。我确定是因为我使用了runTaskLater;我之前已经开始工作了,但是在一个循环中它表现得与众不同。我不知道为什么会发生这种情况,有人能看错吗?
答案 0 :(得分:1)
BukkitRunnable
类在您想要安排稍微复杂的任务的情况下非常有用(例如,在某个操作完成后取消任务会更加容易,因为BukkitRunnable可以自行取消)。
火焰实体的targetSelector
设置为瞄准最近的人类实体,因此产生玩家旁边的火焰不可避免地会导致他们很快瞄准玩家并被杀死(使用你的代码时) )。当他们瞄准玩家时,不是杀死火焰,而是不断重新设置他们的目标以便他们只攻击指定的实体。
更优雅的解决方案是创建没有目标选择器或至少是修改版本的自定义闪光,这样他们就不会经常尝试找到最近的玩家。
我在下面有一些代码,其解释使得火焰攻击玩家在他的视线中看到的最近的实体,持续10秒。注意:从玩家视图中找到最近的实体到实现目标选择的所有内容都可以通过多种不同的方式完成。
这是带有run()方法的BlazeTask类:
public class BlazeTask extends BukkitRunnable {
int ticks = 0; //Tick counter to keep track of how long this task has been running
private Blaze[] blazes; //The blazes
private LivingEntity target; //The target
public BlazeTask(Location location, LivingEntity target, int amount) { //The location where the blazes will be spawned, the target and the amount of blazes you want to spawn
blazes = new Blaze[amount]; //Initialize the blaze array
for (int i = 0; i < blazes.length; i++) { //Spawn as many blazes as we specified at the location
//Also, shift the spawn location by up to a block (random) so that the blazes aren't clumped
blazes[i] = (Blaze) location.getWorld().spawnEntity(location.clone().add(Math.random() - Math.random(), Math.random() - Math.random(), Math.random() - Math.random()), EntityType.BLAZE);
}
this.target = target; //Set the target variable
}
public void run() {
if (++ticks > 20 * 10) { //If this task runs every tick, 200 ticks will equal approximately 10 seconds
cancelTask(); //Cancel the task if this task has been running for 10 seconds or more
}
if (!isValid(target)) { //If the target has died or is null, the blazes have completed their goal
cancelTask(); //Cancel the task (kill the blazes too)
}
int invalidAmount = 0; //Amount of blazes that are either null or dead
for (Blaze blaze : blazes) {
if (isValid(blaze)) { //If the blaze is still there
Entity currentTarget = blaze.getTarget(); //Get the target
if (currentTarget == null || !currentTarget.equals(target)) { //If the blaze doesn't have a target or the target is not the one we want
blaze.setTarget(target); //Re-set the target
}
} else { //If the blaze is dead or gone, increment the counter
invalidAmount++;
}
}
//If for some reason, all the blazes were killed within the 10 seconds, cancel this task
if (invalidAmount == blazes.length) {
this.cancel();
}
}
//This method is used above
private void cancelTask() { //Cancel the task by killing all blazes and also cancelling the task itself
for (Blaze blaze : blazes) {
if (isValid(blaze)) {
blaze.damage(20);
}
}
this.cancel();
}
//This method is also used in the above run() method
private boolean isValid(Entity entity) { //Returns true if an entity is not dead and not null
return entity != null && !entity.isDead();
}
}
以下是我如何使用BlazeTask类以及在玩家愿景中找到最近的实体:
@SuppressWarnings("deprecation")
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
final Player player = event.getPlayer();
if (player.getFoodLevel() < 11) { //This is the reverse of player.getFoodLevel() > 10
return; //If the food level is 10, 9, 8 etc., don't do anything
}
if (player.getItemInHand().getType() != Material.BLAZE_POWDER) {
return; //If the player is NOT holding blaze powder, don't do anything
}
List<Block> sight = player.getLineOfSight((Set) null, 100); //Get the blocks in sight
//Get closest entity code
for (Block block : sight) {
player.playEffect(block.getLocation(), Effect.SMOKE, 1); //Play the smoke effect
List<Entity> entities = (List<Entity>) block.getWorld().getNearbyEntities(block.getLocation(), 2, 2, 2); //Get entities near block
if (entities.isEmpty()) { //If there are no nearby entities, continue to the next block in sight
continue;
}
for (Entity entity : entities) {
if (!(entity instanceof LivingEntity)) { //If the nearby entity is not a LivingEntity, continue onto the next nearby entity
continue;
}
LivingEntity victim = (LivingEntity) entity; //The closest living entity
if (victim.equals(player)) { //If the entity is the player itself, continue onto the next nearby entity
continue;
}
BlazeTask task = new BlazeTask(player.getLocation().clone().add(0, 4, 0), victim, 2); //Initialize the task, the 2 at the end is the number of blazes
task.runTaskTimer(this, 0, 1); //Run the task with a 0 tick delay and a period of 1 tick (run every tick)
player.setFoodLevel(player.getFoodLevel() - 10); //Lower food level
return; //Return so that the loop is exited and we don't keep checking blocks/entities
}
}
}