【问题标题】:move dot between 2 points jframe [closed]在两点jframe之间移动点[关闭]
【发布时间】:2022-01-15 02:58:16
【问题描述】:

第 1 步:任务

好的,我正在尝试在 jframe 中复制它。 https://codepen.io/allanpope/pen/LVWYYd

问题: 我有一个加载屏幕,在 5 秒内我将点从一个圆圈移动到一个图像。就像密码笔一样。除了问题是我不确定如何正确设置动画。 JAVA

我的想法是制作一个像 Move.to(Dot,Duration) 这样的动画函数

我遇到的一个问题是,当使用小数时,点不会在确切的位置,有些根本不会移动。我只是不确定如何制作这个动画功能以及如何结束它。如果有人可以帮助我,那将是史诗般的。我的代码在下面。如果有人能告诉我如何制作贝塞尔曲线动画也会很酷

如果有人想测试我的代码并告诉我有什么问题,那将是史诗般的。

所以首先我有一个函数来制作圆圈位置

package loadingScreen;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
public class LoadTree {

    static BufferedImage tree;
    static int radius =300;
    
    public static List<Dot> dots = new ArrayList<Dot>();   
    
    public static void make() {
        File e = new File("assets/tree.png");
        try {
            tree = ImageIO.read(e);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        for(int x = 0; x<tree.getWidth();x+=1) {
            for(int y = 0; y<tree.getWidth();y+=1) {
                int clr = tree.getRGB(x, y);
                if(clr==0) {
                }else {       
                    int i =(y)*300+x;
                    int a = (i / 4) % 300;
                    int b = (int) Math.floor(Math.floor(i / 300) / 4);                  
                    
                    if (( a % ((1)) == 0) && (b % ((1)) == 0)) {
                        double p = (double) Math.random();
                        int circleX = (int) (MyFrame.width/2+ radius * Math.cos(2 * Math.PI * p));
                        int circleY = (int) (MyFrame.height/2 + radius * Math.sin(2 * Math.PI * p));
                        Dot dot = new Dot(circleX, circleY, clr, 
                                    MyFrame.width/2+x-150, MyFrame.height/2+y-150,circleX,circleY);
                        dots.add(dot);
                    }
                }
            }
        }
    }
}

这里是点类

package loadingScreen;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

public class Dot {

    public int imageY;
    public int imageX;
    public int color;
    public double y;
    public double x;
    public int circleX;
    public int circleY;
    
    public Dot(int x, int y, int color, int imageX, int imageY, int circleX, int circleY){
        this.x = x;
        this.y = y;
        this.color = color;
        this.imageX = imageX;
        this.imageY = imageY;
        this.circleX = circleX;
        this.circleY = circleY;
    }
    
    public void paint(Graphics g){
        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
        Color c = new Color(color);
        g2d.setColor(c);
        g2d.fillRect((int)x,(int) y, 1, 1);
    }
}

这里是 Jpanel

package loadingScreen;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

import loadingScreen.animate.Move;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class MyPanel extends JPanel implements ActionListener{

    Image background;
    Timer timer;
    
    MyPanel(){
        timer = new Timer(0,this);
        timer.start();
        File e = new File("assets/background.png");
        try {
            background = ImageIO.read(e);
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
    
    public void paint(Graphics g) {
        super.paint(g);
        
        Graphics2D g2d = (Graphics2D) g;
        //g2d.drawImage(background, 0, 0, MyFrame.width,MyFrame.height,null);
        
        LoadTree.dots.forEach(dot ->{
            dot.paint(g);
        });
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        LoadTree.dots.forEach(dot ->{
            Move.to(dot, 1000, 0);
            //get slope
        });
        repaint();
        //System.out.println(LoadTree.dots.size());
    }
}

这里是 Jframe

package loadingScreen;

import javax.swing.JFrame;

public class MyFrame extends JFrame{

    MyPanel panel;
    public static int width = 1080;
    public static int height = 720;
    
    MyFrame(){
        panel = new MyPanel();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(panel);
        this.setSize(width, height);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }
}

【问题讨论】:

  • 好吧,显然我很难摆脱一个好的动画挑战,我回去用我个人的“超级简单的动画框架”看看我能实现什么,BounceImagePixel。不过,这没有任何支持;)

标签: java swing animation jframe


【解决方案1】:

您即将进行一次大规模的深潜。动画,好的动画,你没有注意到的动画,真的很难实现,而且是一个非常复杂的主题。

你已经朝着正确的方向开始了。您需要某种“ticker”来告诉您动画何时应该更新,但在那之后它就会崩溃。

您要做的第一件事是摆脱“线性渐进”的概念。也就是说,在每个“滴答”上,对象都会移动一个特定的增量值。这不会产生良好的动画效果,并且当您想要更改动画的速度或持续时间时会很快崩溃。

更好的解决方案是从“基于持续时间的进度”开始。基本上,动画将在固定的时间段内运行,并且在动画的每个滴答声中,您都会根据已过去的时间量和剩余时间量计算对象的新“状态”。

这有利于“规范化”时间线。即动画发生在0-1之间。由此,计算对象应该沿着该时间线的位置变得非常容易。想让它更快或更慢吗?更改时长,剩下的交给你!

首先,弄清楚如何将一个点从一个点移动到另一个点,如果你能移动一个,你就可以移动一千个。

持续时间基础动画引擎...

密切关注:

  • Utilities
  • DurationAnimationEngine

引擎由 Swing Timer 支持,因此在 Swing 中使用是安全的。它的全部目的是在指定的时间段内(尽可能快地运行)并产生随着进展量发生的“滴答声”(记住,标准化时间)

以下是动画的基本实现。很多“工作”发生在mouseClicked 事件中,因为它启动了引擎。当引擎滴答作响时,点会更新。每个点都包裹在AnimatableDot 中,它有一个“from”和“to”点,然后它根据归一化时间计算它的新位置,然后执行一次绘画通道

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class DurationTest {

    public static void main(String[] args) {
        new DurationTest();
    }

    public DurationTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Utilities {

        public static Point2D pointOnCircle(double degress, double radius) {
            double rads = Math.toRadians(degress - 90); // 0 becomes the top
            double xPosy = Math.round((Math.cos(rads) * radius));
            double yPosy = Math.round((Math.sin(rads) * radius));
            return new Point2D.Double(radius + xPosy, radius + yPosy);
        }

        public static Point2D pointOnCircle(double xOffset, double yOffset, double degress, double radius) {
            Point2D poc = pointOnCircle(degress, radius);
            return new Point2D.Double(xOffset + poc.getX(), yOffset + poc.getY());
        }
    }

    public class TestPane extends JPanel {

        private List<AnimatedDot> dots = new ArrayList<>(128);

        private Duration duration = Duration.ofSeconds(5);

        private DurationAnimationEngine engine;

        private List<Color> colors = Arrays.asList(new Color[]{
            Color.RED,
            Color.BLUE,
            Color.CYAN,
            Color.GRAY,
            Color.GREEN,
            Color.LIGHT_GRAY,
            Color.MAGENTA,
            Color.PINK,
            Color.WHITE,
            Color.YELLOW
        });

        public TestPane() {
            Random rnd = new Random();
            setBackground(Color.BLACK);

            for (int index = 0; index < 100; index++) {
                double fromAngle = 360.0 * rnd.nextDouble();
                double toAngle = fromAngle + 180.0;
                Collections.shuffle(colors);
                Color color = colors.get(0);
                dots.add(new AnimatedDot(
                        Utilities.pointOnCircle(fromAngle, 150),
                        Utilities.pointOnCircle(toAngle, 150),
                        color, 2));
            }

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (engine != null) {
                        engine.stop();
                        engine = null;

                        // Reset poisitions
                        for (AnimatedDot dot : dots) {
                            dot.move(0);
                        }
                        repaint();
                        return;
                    }

                    engine = new DurationAnimationEngine(duration, new DurationAnimationEngine.Tickable() {
                        @Override
                        public void animationDidTick(double progress) {
                            for (AnimatedDot dot : dots) {
                                dot.move(progress);
                            }
                            repaint();
                        }
                    });
                    engine.start();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON
            );
            int xOffset = (getWidth() - 300) / 2;
            int yOffset = (getWidth() - 300) / 2;
            g2d.translate(xOffset, yOffset);
            g2d.setColor(Color.DARK_GRAY);
            g2d.drawOval(0, 0, 300, 300);

            for (AnimatedDot dot : dots) {
                dot.paint(g2d);
            }

            g2d.dispose();
        }

    }

    public class DurationAnimationEngine {

        public interface Tickable {

            public void animationDidTick(double progress);
        }

        private Duration duration;
        private Instant timeStarted;

        private Timer timer;

        private Tickable tickable;

        public DurationAnimationEngine(Duration duration, Tickable tickable) {
            this.duration = duration;
            this.tickable = tickable;
        }

        public void start() {
            // You could create the timer lazierly and restarted it as needed
            if (timer != null) {
                return;
            }

            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (timeStarted == null) {
                        timeStarted = Instant.now();
                    }
                    Duration runtime = Duration.between(timeStarted, Instant.now());
                    double progress = Math.min(1.0, runtime.toMillis() / (double) duration.toMillis());

                    tickable.animationDidTick(progress);

                    if (progress >= 1.0) {
                        stop();
                    }
                }
            });
            timer.start();
        }

        public void stop() {
            if (timer == null) {
                return;
            }
            timer.stop();
            timer = null;
        }

    }

    public class AnimatedDot {

        private Dot dot;

        private Point2D from;
        private Point2D to;

        public AnimatedDot(Point2D from, Point2D to, Color color, int radius) {
            dot = new Dot(from.getX(), from.getY(), color, radius);
            this.from = from;
            this.to = to;
        }

        public void paint(Graphics2D g) {
            dot.paint(g);
        }

        public void move(double progress) {
            Point2D pointAt = pointAt(progress);
            dot.setLocation(pointAt);
        }

        public Point2D getFrom() {
            return from;
        }

        public Point2D getTo() {
            return to;
        }

        protected double getFromX() {
            return getFrom().getX();
        }

        protected double getFromY() {
            return getFrom().getY();
        }

        public Double getXDistance() {
            return getTo().getX() - getFrom().getX();
        }

        public Double getYDistance() {
            return getTo().getY() - getFrom().getY();
        }

        protected Point2D pointAt(double progress) {
            double xDistance = getXDistance();
            double yDistance = getYDistance();

            double xValue = Math.round(xDistance * progress);
            double yValue = Math.round(yDistance * progress);

            xValue += getFromX();
            yValue += getFromY();

            return new Point2D.Double(xValue, yValue);
        }
    }

    public class Dot {

        private Color color;
        private double y;
        private double x;
        private int radius;

        private Ellipse2D dot;

        public Dot(double x, double y, Color color, int radius) {
            this.x = x;
            this.y = y;
            this.color = color;
            this.radius = radius;

            dot = new Ellipse2D.Double(0, 0, radius * 2, radius * 2);
        }

        public void setLocation(Point2D point) {
            setLocation(point.getX(), point.getY());
        }

        public void setLocation(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public void paint(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(color);
            g2d.translate(x - radius, y - radius);
            g2d.fill(dot);
            g2d.dispose();
        }

    }
}

好的,所以当我们运行它时,我们得到...

? ...嗯,我想说那是意料之中的,但是一旦我看到它,就很明显出了什么问题。

所有点在相同的时间范围内以相同的速度移动!

那么,答案是什么。嗯,其实有几个……

  1. 我们可以更改每个点的持续时间,以便它们具有单独的持续时间。这会使运动“随机化”,但我不确定它会产生完全相同的效果,因为它们会以不同的速度运动
  2. 我们可以随机化点的开始时间,使它们在不同的时间开始,让它们都具有相同的持续时间(甚至是随机的持续时间)
  3. 我们只能移动一小部分点,但这意味着您可能最终要等待当前子集完成后才能开始下一个子集
  4. 2 和 3 的组合

个性化、随机化的持续时间...

好的,为了简单(和我的理智),我实际上将从 1 开始。每个点都有自己的随机持续时间。这意味着每个点将以不同的速度移动。

密切关注LinearAnimationEngineAnimatedDot#move 方法。

这应该看起来很眼熟,基本上和之前的动画逻辑一样,只是为了点本身而隔离

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class RandomIndividualDuration {

    public static void main(String[] args) {
        new RandomIndividualDuration();
    }

    public RandomIndividualDuration() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class Utilities {

        public static Point2D pointOnCircle(double degress, double radius) {
            double rads = Math.toRadians(degress - 90); // 0 becomes the top
            double xPosy = Math.round((Math.cos(rads) * radius));
            double yPosy = Math.round((Math.sin(rads) * radius));
            return new Point2D.Double(radius + xPosy, radius + yPosy);
        }

        public static Point2D pointOnCircle(double xOffset, double yOffset, double degress, double radius) {
            Point2D poc = pointOnCircle(degress, radius);
            return new Point2D.Double(xOffset + poc.getX(), yOffset + poc.getY());
        }
    }

    public class DurationRange {

        private Duration from;
        private Duration to;

        public DurationRange(Duration from, Duration to) {
            this.from = from;
            this.to = to;
        }

        public Duration getFrom() {
            return from;
        }

        public Duration getTo() {
            return to;
        }

        public Duration getDistance() {
            return Duration.ofNanos(getTo().toNanos() - getFrom().toNanos());
        }

        public Duration valueAt(double progress) {
            Duration distance = getDistance();
            long value = (long) Math.round((double) distance.toNanos() * progress);
            value += getFrom().getNano();
            return Duration.ofNanos(value);
        }
    }

    public class TestPane extends JPanel {

        private List<AnimatedDot> dots = new ArrayList<>(128);

        private Duration duration = Duration.ofSeconds(5);

        private LinearAnimationEngine engine;

        private List<Color> colors = Arrays.asList(new Color[]{
            Color.RED,
            Color.BLUE,
            Color.CYAN,
            Color.GRAY,
            Color.GREEN,
            Color.LIGHT_GRAY,
            Color.MAGENTA,
            Color.PINK,
            Color.WHITE,
            Color.YELLOW
        });

        public TestPane() {
            Random rnd = new Random();
            setBackground(Color.BLACK);

            DurationRange range = new DurationRange(Duration.ofSeconds(1), Duration.ofSeconds(5));

            for (int index = 0; index < 100; index++) {
                double fromAngle = 360.0 * rnd.nextDouble();
                double toAngle = fromAngle + 180.0;
                Collections.shuffle(colors);
                Color color = colors.get(0);

                Duration duration = range.valueAt(rnd.nextDouble());

                dots.add(new AnimatedDot(
                        Utilities.pointOnCircle(fromAngle, 150),
                        Utilities.pointOnCircle(toAngle, 150),
                        color, 2, duration));
            }

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    if (engine != null) {
                        engine.stop();
                        engine = null;
                        reset();
                        return;
                    }

                    System.out.println("Go");

                    List<AnimatedDot> avaliableDots = new ArrayList<>(120);
                    avaliableDots.addAll(dots);

                    engine = new LinearAnimationEngine(new LinearAnimationEngine.Tickable() {
                        @Override
                        public void animationDidTick() {
                            List<AnimatedDot> completed = new ArrayList<>(128);

                            // Reset poisitions
                            for (AnimatedDot dot : avaliableDots) {
                                if (!dot.move()) {
                                    completed.add(dot);
                                }
                            }
                            avaliableDots.removeAll(completed);
                            repaint();

                            if (avaliableDots.isEmpty()) {
                                engine.stop();
                                engine = null;
                                reset();
                            }
                        }
                    });
                    engine.start();
                }
            });
        }

        protected void reset() {
            for (AnimatedDot dot : dots) {
                dot.reset();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON
            );
            int xOffset = (getWidth() - 300) / 2;
            int yOffset = (getWidth() - 300) / 2;
            g2d.translate(xOffset, yOffset);
            g2d.setColor(Color.DARK_GRAY);
            g2d.drawOval(0, 0, 300, 300);

            for (AnimatedDot dot : dots) {
                dot.paint(g2d);
            }

            g2d.dispose();
        }

    }

    public class LinearAnimationEngine {

        public interface Tickable {

            public void animationDidTick();
        }

        private Tickable tickable;
        private Timer timer;

        public LinearAnimationEngine(Tickable tickable) {
            this.tickable = tickable;
        }

        public void start() {
            // You could create the timer lazierly and restarted it as needed
            if (timer != null) {
                return;
            }

            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    tickable.animationDidTick();
                }
            });
            timer.start();
        }

        public void stop() {
            if (timer == null) {
                return;
            }
            timer.stop();
            timer = null;
        }

    }

    public class AnimatedDot {

        private Dot dot;

        private Point2D from;
        private Point2D to;

        private Duration duration;
        private Instant timeStarted;

        public AnimatedDot(Point2D from, Point2D to, Color color, int radius, Duration duration) {
            dot = new Dot(from.getX(), from.getY(), color, radius);
            this.from = from;
            this.to = to;
            this.duration = duration;
        }

        public void paint(Graphics2D g) {
            dot.paint(g);
        }

        public void reset() {
            Point2D futureFrom = to;
            to = from;
            from = futureFrom;
            timeStarted = null;
        }

        public boolean move() {
            if (timeStarted == null) {
                timeStarted = Instant.now();
            }
            Duration runtime = Duration.between(timeStarted, Instant.now());
            double progress = Math.min(1.0, runtime.toMillis() / (double) duration.toMillis());

            Point2D pointAt = pointAt(progress);
            dot.setLocation(pointAt);

            return progress < 1.0;
        }

        public Point2D getFrom() {
            return from;
        }

        public Point2D getTo() {
            return to;
        }

        protected double getFromX() {
            return getFrom().getX();
        }

        protected double getFromY() {
            return getFrom().getY();
        }

        public Double getXDistance() {
            return getTo().getX() - getFrom().getX();
        }

        public Double getYDistance() {
            return getTo().getY() - getFrom().getY();
        }

        protected Point2D pointAt(double progress) {
            double xDistance = getXDistance();
            double yDistance = getYDistance();

            double xValue = Math.round(xDistance * progress);
            double yValue = Math.round(yDistance * progress);

            xValue += getFromX();
            yValue += getFromY();

            return new Point2D.Double(xValue, yValue);
        }
    }

    public class Dot {

        private Color color;
        private double y;
        private double x;
        private int radius;

        private Ellipse2D dot;

        public Dot(double x, double y, Color color, int radius) {
            this.x = x;
            this.y = y;
            this.color = color;
            this.radius = radius;

            dot = new Ellipse2D.Double(0, 0, radius * 2, radius * 2);
        }

        public void setLocation(Point2D point) {
            setLocation(point.getX(), point.getY());
        }

        public void setLocation(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public void paint(Graphics2D g) {
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(color);
            g2d.translate(x - radius, y - radius);
            g2d.fill(dot);
            g2d.dispose();
        }

    }
}

现在,当我们运行它时,我们得到...

嗯,至少它现在更加“随机”了,这就是我认为第 2 点和第 3 点可能更好的组合。

但他们没有反弹?!

啊,好吧,实际上,再次单击第二个示例!点将从当前位置(原点到点)移动并返回到原点。太棒了,从概念上讲,它是可行的。

但是当我点击它时,它们并不是来自一张漂亮的图片!

? ... 所以上面的例子演示了如何在指定的时间段内将一个对象从 A 点动画到 B 点,形成图片只是改变目标目的地(假设你知道它是从什么开始的)。根据我的观察,在移动到最终图片位置之前,首先允许移动点移动到其当前的“结束”位置,因为试图计算弯曲路径会让我?。

缺少什么...

是的,有些东西不见了,你可能看不到,但它对我来说真的很突出。

每个点开始缓慢,然后加速,然后减速到位。这被称为“地役权”(或者,在这种情况下,是缓入/缓出),它并不是世界上最容易实现的东西。有兴趣的可以看看How can I implement easing functions with a thread

现在,您的问题的实际答案是什么?除非你完全疯了(我也是),否则不要试图自己做这种事情,除非你有一个非常具体的理由这样做。相反,请使用其他现成的引擎之一,例如:

上面使用的大部分概念都来自我的动画游乐场源Super Simple Swing Animation Framework。这是我经常玩耍和实验的地方

【讨论】:

  • 非常感谢,我不知道 Java 有 tweenlite。我肯定会查看您的所有代码。谢谢:)
  • @user14101905 好吧,不看全,看好东西??
  • 快速提问你会不会碰巧知道补间引擎是如何工作的,因为我现在一切正常,但我不知道如何让 Tween.to 将点移动到某个点
  • @user14101905 不,我没有真正亲自使用过
  • @user14101905 我玩过补间引擎,文档很难找到。看看BasicUnversialTweenEngineTest 一个非常基本的例子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多