Login
芋圆社区 > 编程 > 【项目】桌宠之旅 > 11-实现桌宠随机动作

11-实现桌宠随机动作

793
0
2023-07-08
2023-07-18
Hey、小怪兽

  • • 之前获取每一帧桌宠图片的时候,可以发现是有很多动作的,把这些动作整理一下,5分钟做一次动作,这样桌宠在桌面的时候就不会单纯站立了
  • • 将动作的图片放到/src/modules/kkr/action文件夹下:
  • • 同时修改/src/modules/kkr/kkr.js里的配置:
  • export const action_config = [
        {
            index: 0,
            name: "dear_jump",
            frames: 66,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 1,
            name: "dear_smile",
            frames: 64,
            texture: [],
            object: {
                loop: false,
            },
        },
        ...省略其他动作
    ]
  • • 回到渲染进程/src/_renderer/renderer.js,添加一些参数:
  • // 动作配置
    let action_config = null;
    // 存储的随机动作索引,用来给定时器随机
    let anim_action_random_index = [];
    let anim_action_random_cache = [];
    // 动画的定时器
    let timer = null;
  • • 接着获取配置的时候也要同时获取动作配置:
  • // 获取配置
    const module = await import(`../modules/${current_module}/${current_module}.js`);
    init_config = module.init_config;
    action_config = module.action_config;
  • • 获取动作的索引值,这样有利于后面随机获取哪个动作:
  • // 将动作的配置转换成随机索引赋值 [0,1,2,3,4]
    anim_action_random_index = Array.from(
        action_config.map((obj, index) => {
            obj.index = index;
            return obj;
        }),
        ({ index }) => index
    );
  • • 开启定时器,进行随机动作:
  • // 开启定时器
    setIntervalTimer();
  • • 这个定时器方法也要写一下:
  • - 如果在拖拽状态的话就不会进行动作,因为拖拽的优先级最高
  • - 随机获取一个动作的索引,就是上面的一个索引数组,通过这个索引可以拿到动作的对象
  • - 检查一下有没有缓存过这个动作,如果有的话就直接播,没有的话就要重新创建动作的动画
  • - 1000 * 15就是15秒的意思,15秒做一个动作
  • // 设置定时器,用来一定时间播放一次随机动作
    const setIntervalTimer = () => {
        timer = setInterval(() => {
            if (drag_static === false) {
                // 随机获取动作的index
                const index = Math.floor(Math.random() * anim_action_random_index.length);
                // 通过index获取动作的配置
                const action = action_config[anim_action_random_index[index]];
                // 如果有缓存的动作就不需要生成了
                const cacheAction = anim_action_random_cache.find((obj) => obj.name === action.name);
    
                if (cacheAction) {
                    change_anim_func(anim_normal, cacheAction, 0);
                } else {
                    create_anim_func(action, 1);
                }
            } else {
                // 说明是在拖拽,什么都不做
            }
        }, 1000 * 60 * 5);
    };
  • • 随机动作的create_anim_func(action, 1)创建加了一个type,这个type是代表是否是初始化
  • • 先去修改一下normal初始状态和drap拖拽的创建方法,后面多传一个0就行了:
  • anim_normal = await create_anim_func(init_config[0], 0);
  • anim_drap = await create_anim_func(init_config.find((obj) => obj.name === "drap"), 0);
  • • 接着修改create_anim_func的传入值:
  • // 创建动画的方法 obj-配置对象, type-是否初始化0/1
    const create_anim_func = async (obj, type)
  • • 还有贴图生成,因为动作的路径多了一个action:
  • const texture_name = type === 0 ? `${file_prefix}/${current_module}/${name}/${num}${file_format}` : `${file_prefix}/${current_module}/action/${name}/${num}${file_format}`;
  • • 写上随机动作完成后进入普通动画的方法:
  • if (object.loop === false) {
        anim.onComplete = () => {
            // 完成动作后500毫秒后进入普通动画
            change_anim_func(anim, anim_normal, object?.endTime ?? 100);
        };
    }
  • • 修改返回值,如果是要切换动作的话就先加入缓存,然后1秒后播放这个动作:
  • if (type === 1) {
        // 缓存随机动作,这样下次不需要再次生成
        anim_action_random_cache.push(anim);
        // 给生成动画时间
        setTimeout(() => {
            // 生成动画后1秒后进入动作动画
            if (drag_static === false) {
                change_anim_func(anim_normal, anim, 0);
            }
        }, 1000);
    } else {
        // 如果是初始动画的话就返回动画
        return anim;
    }
  • • 最后在终端输入pnpm start,等待15秒:
  • • 完美!已经完成了随机动作的切换!
  • • 这里优势就体现出来了,因为可以通过添加和删除帧图片来控制动作,如果会PhotoShop或Spine的话可以自己加动作,格式还是000.png这样:
  • • 同时可以控制动作完成后要保留的时间,在kkr.js配置就行了,像这样配置的话,就会做完这个动作后,0.8秒后才会进入普通状态:
  • object: {
        loop: false,
        endTime: 800,
    },
  • • 如果没配置就0.1秒进入:
  • change_anim_func(anim, anim_normal, object?.endTime ?? 100);
  • • 上面配了随机动作时间是1000 * 15,15秒做一次动作,有点太频繁了,身为桌宠就应该安安静静的,我的定时器这里改成了5分钟1000 * 60 * 5
  • • /src/modules/kkr/kkr.js完整代码:
  • export const init_config = [
        {
            index: 0,
            name: "normal",
            frames: 60,
            texture: [],
            object: {
                loop: true,
            },
        },
        {
            index: 1,
            name: "drap",
            frames: 54,
            texture: [],
            object: {
                loop: true,
            },
        },
    ];
    
    export const action_config = [
        {
            index: 0,
            name: "dear_jump",
            frames: 66,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 1,
            name: "dear_smile",
            frames: 64,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 2,
            name: "high_touch",
            frames: 51,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 3,
            name: "joy_long",
            frames: 108,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 4,
            name: "joy_short",
            frames: 54,
            texture: [],
            object: {
                loop: false,
            },
        },
    
        {
            index: 5,
            name: "salute_smile",
            frames: 94,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 6,
            name: "stand_by",
            frames: 54,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 7,
            name: "weapon_joy",
            frames: 107,
            texture: [],
            object: {
                loop: false,
                endTime: 800,
            },
        },
        {
            index: 8,
            name: "weapon_skill",
            frames: 80,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 9,
            name: "weapon_ub",
            frames: 247,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 10,
            name: "cheer_joy",
            frames: 54,
            texture: [],
            object: {
                loop: false,
            },
        },
        {
            index: 11,
            name: "mana_jump",
            frames: 31,
            texture: [],
            object: {
                loop: false,
            },
        },
    ];
  • • 渲染进程/src/_renderer/renderer.js完整代码:
  • // 导入方法
    import { get_pixel_color_func, body_area_func } from "../utils/anim.js";
    // 当前使用的模型
    let current_module = "kkr";
    // 初始配置和动作配置
    let init_config = null;
    let action_config = null;
    // 当前的动画
    let anim_current = null;
    // 初始动画
    let anim_normal = null;
    // 拖拽动画
    let anim_drap = null;
    // 存储的随机动作索引,用来给定时器随机
    let anim_action_random_index = [];
    let anim_action_random_cache = [];
    // 动画的定时器
    let timer = null;
    // 拖拽状态
    let drag_static = false;
    // 动画的舞台
    let app = null;
    // 动画的canvas
    let anim_canvas = null;
    // 虚拟的canvas
    let shadow_canvas = null;
    // 获取窗口的大小
    const bower_width = window.innerWidth;
    const bower_height = window.innerHeight;
    
    // 页面加载完成执行
    window.addEventListener("load", async (event) => {
        // 获取配置
        const module = await import(`../modules/${current_module}/${current_module}.js`);
        init_config = module.init_config;
        action_config = module.action_config;
    
        // 赋值canvas
        anim_canvas = document.getElementById("taro-canvas");
        shadow_canvas = document.getElementById("shadow-canvas");
        shadow_canvas.width = bower_width;
        shadow_canvas.height = bower_height;
    
        // 动画舞台配置
        app = new PIXI.Application({
            view: anim_canvas,
            width: bower_width,
            height: bower_height,
            backgroundAlpha: 0,
            resolution: 1,
        });
        // 添加给div-taropet元素
        document.getElementById("taro-pet").appendChild(app.view);
    
    
        // 先把初始的动画加载完成
        anim_normal = await create_anim_func(init_config[0], 0);
        anim_normal.play();
        app.stage.addChild(anim_normal);
        // 赋值给当前动画
        anim_current = anim_normal;
    
        // 开始初始化其他的动画
        anim_drap = await create_anim_func(init_config.find((obj) => obj.name === "drap"), 0);
    
        // 将动作的配置转换成随机索引赋值 [0,1,2,3,4]
        anim_action_random_index = Array.from(
            action_config.map((obj, index) => {
                obj.index = index;
                return obj;
            }),
            ({ index }) => index
        );
    
        // 开启定时器
        setIntervalTimer();
    });
    
    // 创建动画的方法 obj-配置对象, type-是否初始化0/1
    const create_anim_func = async (obj, type) => {
        // 存放文件前缀, 文件格式(png,jpg)
        const file_prefix = "./modules";
        const file_format = ".png";
        const { name, frames, object } = obj;
        const texture_array = [];
        // 通过帧数循环获取贴图
        for (let i = 0; i < frames; i++) {
            const num = `000${i}`.slice(-3);
            // texture_name ./modules/kkr/normal/001.png
            const texture_name = type === 0 ? `${file_prefix}/${current_module}/${name}/${num}${file_format}` : `${file_prefix}/${current_module}/action/${name}/${num}${file_format}`;
            const texture = await PIXI.Texture.from(texture_name);
            texture_array.push(texture);
        }
        // 生成动画,配置动画属性
        const anim = new PIXI.AnimatedSprite(texture_array);
        anim.name = name;
        anim.animationSpeed = 0.5;
        anim.loop = object.loop;
    
        // 设置交互模式
        anim.eventMode = "dynamic";
    
        // 鼠标移动事件
        anim.on("mousemove", (event) => {
            const global_position = event.data.global;
            const local_position = anim.toLocal(global_position);
            // 当前这一帧的动画贴图
            const anim_img = anim.texture.baseTexture.resource.source;
    
            if (drag_static) {
                // 这个时候在拖拽,什么都不做
            } else {
                body_area_func(get_pixel_color_func(local_position.x, local_position.y, anim_img), local_position)
            }
        });
        // 鼠标左键点击事件
        anim.on("click", (event) => {
            const global_position = event.data.global;
            const local_position = anim.toLocal(global_position);
            console.log('鼠标左键点击:', local_position)
        });
    
        // 鼠标点击右键拖拽
        anim.on("rightclick", (event) => {
            const global_position = event.data.global;
            const local_position = anim.toLocal(global_position);
            if (drag_static === false) {
                // 如果没在拖拽状态,右键后进入推拽状态,传给主进程点击的位置
                window.mouseAPI.mouseDrapStart({
                    x: local_position.x,
                    y: local_position.y,
                    drap: true,
                });
                // 开启拖拽状态进入拖拽动画
                drag_static = true;
                change_anim_func(anim_current, anim_drap, 0);
            } else {
                // 再次点击脱离拖拽状态
                window.mouseAPI.mouseDrapEnd({
                    drap: false,
                });
                // 取消拖拽状态进入普通动画
                drag_static = false;
                change_anim_func(anim_drap, anim_normal, 0);
            }
        });
    
        if (object.loop === false) {
            anim.onComplete = () => {
                // 完成动作后500毫秒后进入普通动画
                change_anim_func(anim, anim_normal, object?.endTime ?? 100);
            };
        }
    
        if (type === 1) {
            // 缓存随机动作,这样下次不需要再次生成
            anim_action_random_cache.push(anim);
            // 给生成动画时间
            setTimeout(() => {
                // 生成动画后1秒后进入动作动画
                if (drag_static === false) {
                    change_anim_func(anim_normal, anim, 0);
                }
            }, 1000);
        } else {
            // 如果是初始动画的话就返回动画
            return anim;
        }
    };
    
    // 进入动画,可以用来切换动画(切换回normal或进入drap)
    const change_anim_func = (from_anim, to_anim, time) => {
        from_anim.stop();
        setTimeout(() => {
            app.stage.removeChild(from_anim);
            to_anim.gotoAndPlay(0);
            app.stage.addChild(to_anim);
            // 替换当前动画
            anim_current = to_anim;
        }, time);
    };
    
    // 设置定时器,用来一定时间播放一次随机动作
    const setIntervalTimer = () => {
        timer = setInterval(() => {
            if (drag_static === false) {
                // 随机获取动作的index
                const index = Math.floor(Math.random() * anim_action_random_index.length);
                // 通过index获取动作的配置
                const action = action_config[anim_action_random_index[index]];
                // 如果有缓存的动作就不需要生成了
                const cacheAction = anim_action_random_cache.find((obj) => obj.name === action.name);
    
                if (cacheAction) {
                    change_anim_func(anim_normal, cacheAction, 0);
                } else {
                    create_anim_func(action, 1);
                }
            } else {
                // 说明是在拖拽,什么都不做
            }
        }, 1000 * 60 * 5);
    };

上一篇:10-实现桌宠拖拽

下一篇:12-实现桌宠播放语音

Message Board
回复
回复内容不允许为空
留言字数要大于2,小于200!
提交成功,5s后刷新页面!
编程导航

0-桌宠开发前言

1-搭建环境和创建简单的应用程序

2-修改项目结构

3-创建一个透明的窗口

4-nodemon实现热加载

5-获取桌宠每一帧图片

6-Pixi.js加载桌宠初始动画

7-添加鼠标事件

8-获取区域颜色和判断桌宠本体

9-设置进程通信和鼠标穿透

10-实现桌宠拖拽

11-实现桌宠随机动作

12-实现桌宠播放语音

13-实现桌宠词板功能

14-设置系统托盘

15-实现桌宠模组切换

16-桌宠数据和状态拓展

17-生成ICO图标和准备打包

18-完成开发并打包桌宠应用

Copyright © 2020 芋圆社区

Powered by 浙ICP备2020039309号-1

此页面不支持夜间模式!

已进入夜间模式!

已进入普通模式!

搜索框不允许为空

签到成功!经验+5!芋圆币+2!

签到失败!今日已签到!

需要登录社区账号才可以进入!

复制成功
寄,页面未加载完成或页面无锚点