精华内容
下载资源
问答
  • 随机运动生成器,允许机器人自主移动并显示动画。 所有伺服电机的速度控制,有助于平稳的加速和减速。 无阻塞串行解析,可远程控制机器人的运动。 使用分压器电路监控电池电量。 Raspberry Pi Web服务器(web_...
  • 文章目录前言三、敌人3.1创建机器人步骤1 创建脚本**步骤2 定义生命周期****步骤3添加射击动画**3.2发射机器人导弹步骤1 创建导弹运动逻辑步骤2 添加导弹发射方法3.3增加伤害步骤1 添加玩家受击方法3.4创建补给3.5...


    前言

    不了解或者看不懂的可以去看
    unity实战:狂暴机器人游戏教程(上篇)


    提示:以下是本篇文章正文内容,下面案例可供参考

    三、敌人

    3.1创建机器人

    步骤1 创建脚本

    单击“Resources”文件夹,然后将RedRobot、YellowRobot和BlueRobot拖到玩家面前的某个位置(位于地面之上)
    如下图所示。
    在这里插入图片描述
    同时选中RedRobot、YellowRobot和BlueRobot游戏对象,在检视视图中单击Add Component按钮,然后选择New Script命令。命名脚本为Robot。这将在Assets文件夹中创建一个新脚本。将Robot脚本从Assets文件夹移动到Scripts文件夹。在代码编辑器中打开Robot脚本。添加以下变量。

    [SerializeField]private string robotType; 
    public int health; 
    public int range; 
    public float fireRate; 
    public Transform missileFireSpot; 
    UnityEngine.AI.NavMeshAgent agent; 
    private Transform player; 
    private float timeLastFired; 
    private bool isDead;
    

    robotType表示机器人的类型:RedRobot、BlueRobot或YellowRobot。
    health是机器人生命值,
    range是它能射击的距离,
    fireRate 是它能射击的速度。
    agent是对NavMeshAgent组件的引用,
    player是机器人跟踪的对象,
    isDead表示机器人是否死亡。

    保存脚本并切换回Unity。单击RedRobot游戏对象,然后在 robotType字段中输入 RedRobot。将Health设置为 14,Range值为 150,Fire Rate为2。单击Apply按钮,将这些更改应用到预置。如下图所示。
    在这里插入图片描述
    对YellowBot执行相同的操作:Robot Type设为YellowRobot,Health设为20,Range设为300,Fire Rate设为3。
    单击Apply按钮。
    对BlueRobot执行相同的操作:Robot Type设为BlueRobot,Health设为10,Range设为200,Fire Rate设为1。
    单击Apply按钮。
    Missile Fire Spot参数是机器人发射导弹的位置。

    单击RedRobot游戏对象旁边的箭头以显示其子对象。拖动其子游戏对象 MissileFireSpot 到Missile Fire Spot属性栏。
    对YellowBot和BlueRobot执行同样的操作。如下图所示。
    在这里插入图片描述

    步骤2 定义生命周期

    现在在代码编辑器中打开Robot脚本,修改代码。

    void Start() {// 1
    isDead = false;
    agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
    player = GameObject.FindGameObjectWithTag("Player").transform;} // Update is called once per frame 
    void Update() {// 2
    if (isDead) { return;}
    // 3    transform.LookAt(player); 
    // 4    agent.SetDestination(player.position); 
    // 5    if (Vector3.Distance(transform.position,player.position) < range && Time.time - timeLastFired > fireRate) {
    // 6    timeLastFired = Time.time; fire();}} 
    private void fire() { 
            Debug.Log("Fire");}
    

    下面是对应编号代码中操作的解释:
    ①默认情况下,所有的机器人都是活的。然后将代理和player值分别设置为 NavMesh 代理和player组件。
    ②检查机器人是否已死。
    ③让机器人面对玩家。
    ④告诉机器人使用 NavMesh 找到玩家。
    ⑤检查玩家是否在射击范围内,并且是否攻击冷却完毕。
    ⑥更新timeLastFired到当前时间,并调用Fire方法,它只是暂时将消息记录到控制台。
    接下来需要为玩家游戏对象分配一个合适的标签,以便于让机器人找到它。
    保存脚本,回到Unity。点击Player游戏物体,点击标签下拉菜单,并选择Player标签。
    运行游戏,将看到机器人向玩家移动,并且“Fire”日志不断出现在控制台中,如下图所示。
    在这里插入图片描述
    现在机器人在向玩家静态地移动。如果他们脚下的轮子滚起来的话,看起来会好得多。而且,机器人在向玩家开火时也应该有射击动作。
    步骤1 添加足部动画
    在随便哪个机器人上单击一个 RobotRampage_BotBall_v1 的游戏对象(展开其子对象上的箭头找到它),
    然后转到Window\Animation以打开Animation窗口。
    单击Create按钮: 在名称处输入Ball,保存到Animations文件夹下,然后单击保存按钮。
    单击AddProperty按钮,单击Transform旁边的箭头,然后单击Rotation旁边的+号,如图下所示。
    在这里插入图片描述
    一对关键帧(灰色菱形)将出现在时间轴上的1秒的位置。

    单击最后一个关键帧,并注意到游戏对象的Rotation字段已在检视视图的转换组件中变为红色。将Rotation X值设置为-360。这将导致球旋转360度。
    同时选择其他两个机器人的RobotRampage_BotBall_v1子游戏对象。选中所有RobotRampage_BotBall_v1游戏后,将RobotRampage_BotBall_v1拖到检视视图中。如下图所示。
    在这里插入图片描述
    运行游戏,会看到机器人脚下的球在旋转

    步骤3添加射击动画

    全选三个RobotRampage_Bot游戏对象,并通过单击Controller字段旁边的圆圈选择Robot,如下图所示。
    在这里插入图片描述

    逐个选择每个RobotRampage_Bot,单击Prefab栏的Apply按钮,应用对预制体的修改,如下图所示。
    在这里插入图片描述
    现在打开Robot脚本。添加变量,然后修改Fire ()方法:

    public Animator robot;
    private void fire() { robot.Play("Fire");}
    

    保存脚本,回到Unity。选择YellowRobot并将子物体RobotRampage_Bot拖入Robot属性。应用对预制体的修改。
    如下图所示。
    在这里插入图片描述
    在RedRobot和BlueRobot上执行同样的操作。确保在完成操作后应用对预制体的更改。
    运行游戏,看到机器人开火了

    3.2发射机器人导弹

    步骤1 创建导弹运动逻辑

    新建一个C#脚本,命名为“Missile”。
    将RobotMissileBlue、RobotMissileRed和RobotMissileYellow的预制体从项目视图的Resources文件夹拖到层级视图上。
    保持选中层级视图中的RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象,然后将Missile脚本拖到检视面板。
    在代码编辑器中打开Missile脚本并添加以下变量:

    public float speed = 30f; 
    public int damage = 10;
    

    speed是导弹飞行的快慢。
    damage是导弹命中玩家时造成的伤害。

    现在,在damage变量下面添加以下方法:

    //1
    void Start() { 
    StartCoroutine("deathTimer");} 
    // 2
    void Update() {
    transform.Translate(Vector3.forward * speed * Time.deltaTime);
    } 
    // 3
    IEnumerator deathTimer() {
    yield return new WaitForSeconds(10); Destroy(gameObject);
    }
    

    在计算机术语中,会经常听到“线程”这个词。这是一种让计算机同时做多种事情的方法。在Unity中,可以使用coroutines(协程)来模拟线程。

    协同方法返回IEnumerator。这些决定了协同的持续时间。下面是代码对应的注释:
    ①当实例化一个导弹,将开始一个名为“deathTimer”的协同方法。
    ②每帧向正前方移动相应的距离。
    ③该方法立即返回一个 WaitForSeconds,设置为10。意为该方法将在yield语句等待十秒后恢复。如果导弹没有击中球员,它将会自毁。
    保存脚本,回到Unity。依次单击RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象上的Prefab栏的Apply按钮,应用对预制体的更改。

    步骤2 添加导弹发射方法

    打开Robot脚本。在该文件的顶部,在类的“{”下面添加以下内容:

    [SerializeField] GameObject missileprefab;
    

    missilePrefab 是导弹的预制体。修改Fire方法,代码如下。

    private void fire() {
    GameObject missile = Instantiate(missileprefab); missile.transform.position=missileFireSpot.transform.position; missile.transform.rotation=missileFireSpot.transform.rotation; robot.Play("Fire");
    }
    

    这将实例化一个新的 missilePrefab,并设置其位置和旋转到机器人的射击点。保存脚本回到Unity。

    从层级视图中删除RobotMissileBlue、RobotMissileRed和RobotMissileYellow游戏对象。
    单击RedRobot游戏对象,将RobotMissileRed预置体从Resources文件夹拖到Robot组件的Missile prefab栏中,然后单击Apply按钮。如图3-3-1所示。
    在这里插入图片描述
    在YellowRobot和BlueRobot游戏对象上执行同样的操作,确保最后单击了Apply按钮。
    运行游戏,看到机器人发射导弹

    3.3增加伤害

    步骤1 添加玩家受击方法

    导弹会伤害到玩家,玩家也会损坏机器人。新建一个新的C#脚本,命名它Player并添加以下变量:

    public int health; 
    public int armor; 
    public GameUI gameUI;
    private GunEquipper gunEquipper; 
    private Ammo ammo;
    

    health是玩家的剩余生命值。当生命值为零,游戏结束。
    armor是一个玩家的装甲,减少50%的伤害。
    一旦装甲变成0,玩家将受到100%伤害。
    gameUI和gunEquipper是对脚本的引用。
    ammo是提前建立的弹药类。将以下内容添加到Start方法:

    void Start () {
    ammo = GetComponent<Ammo>();
    gunEquipper = GetComponent<GunEquipper>();
    }
    

    这只是初始化对Ammo和GunEquipper组件的引用。添加以下方法:

    public void TakeDamage(int amount) { 
    int healthDamage = amount;
    if (armor > 0) {
    int effectiveArmor = armor * 2; effectiveArmor -= healthDamage;
    if (effectiveArmor > 0) { 
    armor = effectiveArmor / 2; 
    return;}
    armor = 0;
    }health -= healthDamage; 
    Debug.Log("Health is " + health); 
    if (health <= 0) { Debug.Log("GameOver");}}
    

    TakeDamage ()根据玩家剩余的装甲量来计算伤害减免。
    如果玩家没有盔甲,那么将会受到完整伤害。
    如果生命值达到0,游戏将会结束;
    现在,只需要将其记录到控制台。
    保存脚本,回到Unity。
    将Player脚本添加到Player游戏对象。
    设置Health为100,Armor为 20,
    并拖动GameUI游戏对象到GameUI字段。

    步骤2 导弹检测碰撞

    当导弹与玩家的碰撞器相撞,它将通过OnCollisionEnter()处理伤害玩家的计算。在代码编辑器中打开Missile脚本并添加以下方法:

    void OnCollisionEnter(Collision collider) {
    if(collider.gameObject.GetComponent<Player>()!=null&&collider.gameObject.tag == "Player") {
    collider.gameObject.GetComponent<Player>().TakeDamage(damage);
    }
    Destroy(gameObject);
    }
    

    导弹通过检查与它碰撞的游戏对象Tag是否为Player来判断是否击中玩家。
    它还要检查是否Player脚本处于激活状态,因为Player组件将在游戏结束后被禁用。
    如果两个条件都满足,它将通过调用Player脚本上的TakeDamage方法来计算伤害。
    导弹还会同时销毁自身。
    保存脚本,回到Unity。

    运行游戏。让炮弹击中自己,查看控制台播放的玩家受击信息
    在这里插入图片描述

    步骤3 添加机器人受击方法

    当玩家的生命值到0时,记录游戏结束。稍后,将会实现更好的处理方法。
    目前机器人仍然是无敌的。因为它们还没有添加受击方法。打开Robot脚本并添加以下方法:

    // 1
    public void TakeDamage(int amount) { 
    if (isDead) {return;}
    health -= amount; 
    if (health <= 0) 
    {isDead = true; 
    robot.Play("Die"); 
    StartCoroutine("DestroyRobot");}} 
    // 2
    IEnumerator DestroyRobot() {
    yield return new WaitForSeconds(1.5f); 
    Destroy(gameObject);}
    

    ①这与玩家TakeDamage()方法的逻辑大致相同。唯一不同的地方是,机器人将在生命值为0的时候先播放死亡动画再调用DestroyRobot ()销毁自身。
    ②这在摧毁机器人之前增加了一个1.5秒的延迟,为Die动画播放完成提供了足够的时间。
    保存脚本。

    步骤4 实现聚焦

    不同的枪支应该有不同的攻击范围、破坏力以及通过鼠标右键可以实现不同程度的聚焦。
    打开Gun脚本,并添加以下变量:

    public float zoomFactor; 
    public int range;
    public int damage;
    private float zoomFOV; 
    private float zoomSpeed = 6;
    

    当玩家点击鼠标右键时,zoomFactor控制缩放级别。
    zoomFOV是最终缩放视角。
    range是枪的射程。猎枪的射程最短,而手枪最长。
    damage是枪造成的伤害。
    保存脚本回到Unity。
    单击1Pistol游戏对象,并将其ZoomFactor设置为1.3,Range为60,Damage为3。
    单击2AssaultRifle游戏对象,并将其ZoomFactor设置为1.4,Range为30,Damage为1。
    单击3Shotgun游戏对象,并将其ZoomFactor设置为1.1,Range为10,Damage为10。

    打开Gun脚本并更新Start ()方法到以下内容

    void Start() {
    zoomFOV = Constants.CameraDefaultZoom / zoomFactor; 
    lastFireTime = Time.time - 10;}
    

    这只是初始化了缩放因子。修改Update()方法,代码如下。

    protected virtual void Update() {
    // Right Click (Zoom)
    if (Input.GetMouseButton(1)) {
    Camera.main.fieldOfView=Mathf.Lerp(Camera.main.fieldOfView,zoomFOV,zoomSpeed * Time.deltaTime);
    } else {
    Camera.main.fieldOfView = Constants.CameraDefaultZoom;}}
    

    如果玩家点击鼠标右键,这将通过 Mathf Lerp平滑地播放缩放效果。

    步骤5 添加射线

    为了确定机器人是否被击中,将使用光线。光线是一个隐形的射线,但是可以检测到碰撞。首先,必须定义命中的方法。添加以下内容:

    private void processHit(GameObject hitObject) {
    if (hitObject.GetComponent<Player>() != null) {hitObject.GetComponent<Player>().TakeDamage(damage);}
    if(hitObject.GetComponent<Robot>()!=null){ hitObject.GetComponent<Robot>().TakeDamage(damage);}}
    

    这个方法将伤害值传递给正确的游戏对象。为了实现光线,将以下内容添加到Fire方法的底部。

    Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f0.5f0)); 
    RaycastHit hit;
    if (Physics.Raycast(ray,out hit,range)) { processHit(hit.collider.gameObject);}
    

    这将创建一个射线,并检查射线的命中。道理很简单,如果射线在枪的射程内与一个游戏对象碰撞就会触发processHit方法。
    processHit判断命中的是否是机器人,如果是,机器人将会受到伤害。
    保存脚本,回到Unity。运行游戏,按住鼠标右键缩放,然后攻击那些机器人,直到他们死掉。

    3.4创建补给

    步骤1 添加拾取补给的方法

    玩家需要一种方法来恢复生命值、护甲和弹药。案例将在场景的随机位置添加一些漂浮的补给物让玩家拾取。在代码编辑器中打开Player脚本,并添加以下内容:

    // 1
    private void pickupHealth() { 
    health += 50;
    if (health > 200) {health = 200;}} 
    private void pickupArmor() { armor += 15;} 
    // 2
    private void pickupAssaultRifleAmmo() { ammo.AddAmmo(Constants.AssaultRifle,50);} 
    private void pickupPisolAmmo() { ammo.AddAmmo(Constants.Pistol,20);} private void pickupShotgunAmmo() { ammo.AddAmmo(Constants.Shotgun,10);}
    

    这些方法实现了玩家捡到补给物时获得的增益效果。
    现在添加以下内容:

    public void PickUpItem(int pickupType) { 
    switch (pickupType) {
    case Constants.PickUpArmor: 
    pickupArmor();break;
    case Constants.PickUpHealth: 
    pickupHealth();break;
    case Constants.PickUpAssaultRifleAmmo: pickupAssaultRifleAmmo();break;
    case Constants.PickUpPistolAmmo: 
    pickupPisolAmmo();break;
    case Constants.PickUpShotgunAmmo: 
    pickupShotgunAmmo();break; default:
    Debug.LogError("Bad pickup type passed" + pickupType); break;}}
    

    PickUpItem方法通过传入int参数,获取正在拾取的补给物的类型。

    步骤2 创建补给逻辑

    Constants文件引用所有补给物类型的ID。这些ID对应了五种类型的补给物。这些ID将作为参数传递到PickUpItem 方法中。
    保存脚本,回到Unity。新建一个C#脚本,命名为“Pickup”,并添加字段,代码如下。

    public int type;
    

    这表示补给物的类型。现在添加以下内容

    void OnTriggerEnter(Collider collider) {
    if(collider.gameObject.GetComponent<Player>()!=null&&collider.gameObject.tag == "Player") {
    collider.gameObject.GetComponent<Player>().PickUpItem(type);Destroy(gameObject);
    }}
    

    这会让补给物监听与玩家的碰撞,从而调用玩家身上Player脚本上的PickUpItem方法,并传入自身的补给类型,然后销毁自身。
    保存脚本,回到Unity。

    在Resources文件夹中,选择PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和PickupArmor预设。选中所有这些选项后,单击脚本类别中的Add Component按钮,选择Pickup脚本。
    接下来,添加Rigidbody。勾选Is Kinematic。
    最后,添加BoxCollider。勾选Is Trigger,并设置Size为(1.5、1.5、1.5)。如下图所示
    在这里插入图片描述
    现在来配置每个补给物的类型。
    选择Resources文件夹中的PickupAmmoPistol,
    并将其Type设置为1,
    将PickupAmmoAssaultRifle设置为2,
    PickupAmmoShotgun为3,
    PickupHealth为4,
    PickupArmor为5。

    步骤3 添加补给的漂浮动画

    现在要做的事情是再补给物上添加旋转和浮动的动画。动画已经预先创建好了,现在只是需要创建Animator。在项目视图的Animations文件夹中创建一个Animator Controller,命名为“Pickup”。
    双击Pickup动画控制器打开Animator视图。在空网格处右击唤出快捷菜单,然后选择Create State\Empty命令。在检视视图中,更名为“Spin”并将Motion设置为cubeSpin,如下图所示。
    在这里插入图片描述
    接下来,返回到项目视图并选择Resources文件夹。
    展开PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和PickupArmor预设,
    选中它们的子物体。
    将项目视图中Animations文件夹里的Pickup拖到检视视图。

    现在,将这五个补给物(不是子物体)复制到场景上,位置略高于地面,如下图所示。
    在这里插入图片描述
    运行游戏。控制玩家移动到每个补给上。
    当主角接触到补给的时候,会获得增益,并且补给物会消失。
    如图所示。下一章将会添加一个UI来查看这些增益效果。

    3.5补给刷新点生成

    步骤1 创建补给点

    在层级视图中删除所有的Pickup和Robot游戏对象。
    创建一个新的C#脚本,将其命名为“PickupSpawn”,并添加字段。

    private GameObject[] pickups;
    

    pickups将保存所有补给类型。现在添加以下方法:

    // 1void spawnPickup() {// Instantiate a random pickup
    GameObject pickup = Instantiate(pickups[Random.Range(0,pickups.Length)]);
    pickup.transform.position = transform.position; pickup.transform.parent = transform;} 
    // 2IEnumerator respawnPickup() {
    yield return new WaitForSeconds(20); spawnPickup();} 
    // 3void Start() { spawnPickup();}
    // 4public void PickupWasPickedUp() { StartCoroutine("respawnPickup");}
    

    下面是每个方法的工作:
    ①实例化一个随机类型的补给,并设置它的位置。
    ②在调用spawnPickup方法之前等待20秒。
    ③在自身实例化时生成一个补给。
    ④当玩家捡起补给时,重置协同方法。
    保存脚本。打开Pickup脚本,然后在Destroy(gameObject)上添加以下行:

    GetComponentInParent<PickupSpawn>().PickupWasPickedUp();
    

    当补给与主角相撞时,将在PickupSpawn脚本上启动生成计时器。
    保存脚本回到Unity。

    创建一个空的游戏对象,命名为“PickupSpawn”,并添加PickupSpawn脚本。
    在这里插入图片描述
    在这里插入图片描述
    选中PickupSpawn的游戏对象,设置其Pickups属性的Size为5。
    Lock检视视图。将Resources文件夹中的PickupAmmoAssaultRifle、PickupAmmoPistol、PickupAmmoShotgun、PickupHealth和 PickupArmor预设分别拖入到Pickups元素中。
    最后,UnLock检视视图。

    步骤2 设置所有补给点

    现在,将PickupSpawn游戏对象拖到Resources文件夹中,创建一个预制体。
    创建一个空的游戏对象,命名为“PickupSpawns”。将其Position设置为 (0、0、0)。将PickupSpawn拖入到PickupSpawns,使其成为PickupSpawns的子物体。然后再复制出六个。
    为了保持整洁,将每个子对象名称都更改为“PickupSpawn”。
    如下图所示。
    在这里插入图片描述
    从最顶部PickupSpawn的游戏对象开始设置位置:
    运行游戏,会看到七个补给生成在场景中。走过去,把它们捡起来。

    步骤3 创建传送点

    现在,补给已经可以自动生成了,下面来实现机器人的自动生成。
    将Teleporter预置从Resources文件夹拖到场景的根目录下。
    创建一个C#脚本,将其命名为“RobotSpawn”。
    在代码编辑器中打开它,并在类的“{”之后添加以下变量:

    [SerializeField] GameObject[] robots;
    private int timesSpawned; 
    private int healthBonus = 0;
    

    Robots保存了将要用来实例化的所有类型的机器人预制体。
    healthBonus 是每个机器人每波获得多少生命值,当玩家活得越久,游戏将变得越难。
    timesSpawned 是机器人的生成周期。

    接下来添加以下方法:

    public void SpawnRobot() { 
    timesSpawned++;
    healthBonus += 1 * timesSpawned;
    GameObject robot = Instantiate(robots[Random.Range(0,robots.Length)]);
    robot.transform.position=transform.position;
    robot.GetComponent<Robot>().health += healthBonus;
    }
    

    SpawnRobot ()方法用来实例化一个机器人,并设置它的生命值和位置。
    保存脚本回到Unity。

    将RobotSpawn脚本拖到Teleporter游戏对象上。在层级视图中选择Teleporter游戏对象,然后将RedRobot、YellowRobot和BlueRobot预制体从Resource文件夹中拖到检视视图的Robots字段中。
    在这里插入图片描述

    步骤4 设置所有传送点

    在层级视图中,创建一个空的游戏对象,将其命名为“RobotSpawns”,并将其位置设置为 (0、0、0)。将Teleporter拖到RobotSpawns中,然后通过该Ctrl+D键复制三个。都重命名为“Teleporter”,如下图所示。
    在这里插入图片描述
    从最上面的Teleporter游戏对象开始,依次设置他们的位置

    步骤5 创建游戏管理器

    创建一个C#脚本,将它命名为“Game”,并将文件的内容替换为:

    private static Game singleton;
    [SerializeField] RobotSpawn[] spawns; 
    public int enemiesLeft;
    

    singleton引用自身,实现单例模式。因为只有一个游戏,所以只需要一个Game对象来跟踪得分、剩余机器人数量和当前的波数。
    这些数据将在下一章实现,现在Game类只实现初始化机器人传送器。
    spawns是机器人传送器的数组,它们每波产生机器人。
    enemiesLeft跟踪游戏中有多少机器人还活着。

    接着添加以下方法:

    // 1
    void Start() { 
    singleton = this; 
    SpawnRobots();} 
    // 2
    private void SpawnRobots() {
    foreach (RobotSpawn spawn in spawns) { 
    spawn.SpawnRobot();enemiesLeft++;
    }}
    

    代码说明如下:
    ①初始化单例并调用SpawnRobots ()。
    ②遍历数组中的每个 RobotSpawn 并调用 SpawnRobot () 来生成一个机器人。
    保存脚本回到Unity。

    创建一个空的游戏对象,命名为“Game”。将Game脚本拖到这个对象上,如下图所示。
    在这里插入图片描述
    选中Game游戏对象并锁住检视视图。将Teleporter游戏对象全部拖到Spawns字段上。
    在这里插入图片描述
    运行游戏,会看到生成了四个机器人

    3.6添加声音

    步骤1 添加音频

    大部分内容已经建立完毕,现在还差一点点的声音。打开Robot脚本并在顶部添加以下内容:

    [SerializeField]private AudioClip deathSound; 
    [SerializeField]private AudioClip fireSound;
    [SerializeField]private AudioClip weakHitSound;
    

    保存脚本,回到Unity。
    在Resources文件夹中通过Ctrl键加鼠标左键同时选中YellowRobot、RedRobot和BlueRobot预制体,
    并将Fire Sound设置为missile、Death Sound设置为deadRobot,以及Weak Hit Sound设置为weakHitSound。

    步骤2 实现声音播放

    打开Robot脚本,在Fire ()方法的“}”前添加如下代码。

    GetComponent<AudioSource>().PlayOneShot(fireSound);
    

    现在,修改 TakeDamage ()方法来播放一些声音。使用如下代码替换掉if (health <= 0)的内容。

    if (health <= 0) { 
    isDead = true; 
    robot.Play("Die");
    StartCoroutine("DestroyRobot");
    GetComponent<AudioSource>().PlayOneShot(deathSound);} 
    else 
    {GetComponent<AudioSource>().PlayOneShot(weakHitSound);}
    

    保存脚本,回到Unity。运行游戏,会听到更多的声音!


    总结

    提示:终于把这个教程做完了,之前有些忙,导致16号才出,不好意思!!!

    展开全文
  • 写字机器人设计上包含了路径规划、直线插补、加减速控制等常用电机运动控制算法,在软件上也会用到DXF文件解析、openCV图像处理等G代码生成工具,对后期深入研究激光切割机、雕刻机、3D打印机等大型设备有很好的铺垫...
  • 疫情期间的防疫机器人需要一个语音讲话生成引擎 也就是TTS语音引擎. 这玩意其实微软的TTS很好用了词语读的连贯没有一个个读的僵硬. 现在在linux下找了一番发现没啥好SDK,公司又一律不买讯飞的SDK.自己写是不可能的....

    疫情期间的防疫机器人需要一个语音讲话生成引擎 也就是TTS语音引擎.

    这玩意其实微软的TTS很好用了词语读的连贯没有一个个读的僵硬. 现在在linux下找了一番发现没啥好SDK,公司又一律不买讯飞的SDK.自己写是不可能的.

    没法就找到了espeak.小巧跨平台可就是生成的执行文件一直播不出声音.

    一番周折发现是系统声卡与portaudio的原因. 把portaudio库安装好就能用了

    命令如下:

    start TTS64.exe --path="." -v zh "你好,我是机器人,虽然我的声音不好听,但我现在会说话了,不信我读书你听,燕子去了,有再来的时候;杨柳枯了,有再青的时候;桃花谢了,有再开的时候。但是,聪明的,你告诉我,我们的日子为什么一去不复返呢,是有人偷了他们罢,那是谁,又藏在何处呢,是他们自己逃走了罢——如今又到了哪里呢."
    pause
    

    -v zh是播放支持中文,不过得是UTF-8编码的字符句子.

    就是说话太难听了.勉强混过关吧.

    没有时间深究,不知道怎么换音源数据,估计得深入源代码.这份代码不知道被修改过没有暂先搁放

    源码   WIN执行例子

    展开全文
  • 穿过狐狸洞潜行穿过机器人鳄鱼 Fox是2D概念验证游戏,于2014年制作而成。您扮演的是狐狸,使用地下隧道偷偷越过敌人的鳄鱼,以拯救您的兄弟。 使用构建。 ( 艺术和( )的声音设计(请参阅)。 注意:此存储库已...
  • 如果您没有可用的mp3音频文件,请使用 (Amazon的文本到语音)以潜在的多种声音为该单词生成音频,并且 将找到该词的所有音频上传到Memrise。 AWS Polly以两种声音(女性码和马氏码)产生近乎完美的日语语音。 我...
  • Vocal是Discord机器人,它根据用户的请求,利用十五的应用程序从流行的虚构人物中发送生成声音。 确保检查出十五个使该项目成为可能的出色工具。 可以在上找到其工具。 配置 在config.json中为漫游器配置以下值: ...
  • 使用 JCaptcha 开发图形和声音验证码 ...JCaptcha 是一个 Java 开源项目,利用 JCaptcha,不但可以生成图形验证码,还可以利用与 FreeTTS 的集成,来生成声音验证码,而盲人则可以通过识别声音验证码,来正常...

    使用 JCaptcha 开发图形和声音验证码

    当前越来越多的网站系统采用 CAPTCHA 验证码,来阻止垃圾信息发布机器人的信息提交,但通常绝大多数网站,只提供图片验证码,而这将影响盲人用户的使用。JCaptcha 是一个 Java 开源项目,利用 JCaptcha,不但可以生成图形验证码,还可以利用与 FreeTTS 的集成,来生成声音验证码,而盲人则可以通过识别声音验证码,来正常登录和使用网站的服务。本文将基于四个示例,首先介绍如何使用 JCaptcha 生成图形验证码,最后还介绍了如何开发生成声音验证码。

    JCaptcha 简介

    CAPTCHA 全称 Completely Automated Public Turing Test to Tell Computers and Humans Apart,最早作为卡内基梅隆大学的一个科研项目,用于生成一个人类容易通过而计算机难以通过的测试,目前广泛应用于网络应用,用于阻止机器人发布垃圾 信息。JCaptcha 即为 Java 版本的 CAPTCHA 项目,其是一个开源项目,支持生成图形和声音版的验证码,在生成声音版的验证码时,需要使用到 FreeTTS。目前,JCaptcha 官方网站显示有 2.0 版本,但二进制版只有 1.0 版可供下载,本文亦是基于 1.0 版本展开。

    一个简单的图形验证码

    JCaptcha 提供了一定的可扩展能力,用于开发人员创建出复杂的图形验证码。下面,首先利用 JCaptcha 提供的 API 来快速开发一个简单示例。本文的示例为 Web 应用,可以运行在 Tomcat 和 WebSphere 上,除了准备 Web 服务器外,我们还需要准备好 JCaptcha 运行时所必须的 Jar 包。

    准备所需的 Jar 包

    JCaptcha 项目在实现中,还引用了 commons-collections 和 commons-logging 两个开源项目,再加上 JCaptcha 本身的实现,我们共需要三个包,具体信息如下:

    • jcaptcha-1.0-all.jar
    • commons-logging-1.1.1.jar
    • commons-collections-3.2.jar

    创建生成图形验证码的 Servlet

    在 示例 1 中,我们采用 Servlet 生成验证码图片,并将这个图片写到 ServletOutputStream 中,同时将 Servlet 的 response 的类型设置为 image/jpeg。在前台页面中,我们使用 img 标签,并将 src 指向这个 Servlet,这样我们就可以在页面中显示这个生成的验证码图片。清单 1 是 Servlet 代码片段。

    清单 1. 生成图形验证码代码片段
     // set content type as jpeg 
     httpServletResponse.setHeader("Cache-Control", "no-store"); 
     httpServletResponse.setHeader("Pragma", "no-cache"); 
     httpServletResponse.setDateHeader("Expires", 0); 
     httpServletResponse.setContentType("image/jpeg"); 
    
     // create the image using session ID 
     logger.fine("tring to get image captcha service"); 
     BufferedImage bufferedImage = service 
            .getImageChallengeForID(httpServletRequest.getSession(true) 
                .getId()); 
    
     ServletOutputStream servletOutputStream = httpServletResponse 
            .getOutputStream(); 
    
     // write the image to the servlet output stream 
     logger.fine("tring to output buffered image to servlet output stream"); 
     ImageIO.write(bufferedImage, "jpg", servletOutputStream); 
      
     try { 
      servletOutputStream.flush(); 
     } finally { 
      servletOutputStream.close(); 
     }

    清单 1 中的 service 对象,我们采用了一个 JCaptcha 的默认实现类 DefaultManageableImageCaptchaService,在代码中,我们需要通过获取 service 对象,根据 Session ID 来生成验证码图片。除了使用该默认实现类外,我们也可以通过实现 ImageCaptchaService 接口来生成更为复杂的验证码图片(参见示例 2)。清单 2 为获取 service 对象的示例代码,JCaptcha 建议使用单例来生成 service 对象。

    清单 2. 生成 ImageCaptchaService 对象
     public class SampleImageCaptchaService  { 
    
     private static ImageCaptchaService instance; 
    
     /** 
     * Get default manageable image captcha service 
     * @return ImageCaptchaService 
     */ 
     public static ImageCaptchaService getInstance() { 
     if (instance == null) { 
        instance = new DefaultManageableImageCaptchaService(); 
      } 
        return instance; 
      } 
     }

    示例 1 提供了 2 个简单的页面,一个用于显示图形验证码,另一个则在验证成功后显示成功信息。清单 3 为用于显示图形验证码的页面代码片段。

    清单 3. 显示图形验证码页面代码片段
     <form action="CaptchaValidationServlet" method="post"> 
     <table> 
      <tr> 
        <td colspan="2"><img src="ImageCaptchaServlet" /></td> 
      </tr> 
      <tr> 
        <td> 请输入您所看到的字符 :</td> 
        <td><input type="text" name="captcha_input" value="" /> 
                    <%=request.getAttribute("ERROR") == null ? "" :       
                    request.getAttribute("ERROR")%></td> 
      </tr> 
      <tr> 
          
        <td><input type="submit" value="提交" /></td> 
      </tr> 
     </table> 
     </form>

    除了实现图形验证码的生成和展示,我们还需要一个 Servlet 来验证用户的输入。用户输入验证的代码的核心,是使用在生成图片时所使用的 service 对象,调用 JCaptcha 的输入验证方法(本示例为 validateResponseForID 方法),来判断用户输入是否正确,清单 4 为输入验证代码片段。

    清单 4. 验证用户输入
     validated = service.validateResponseForID( 
        request.getSession(true).getId(), userCaptchaResponse).booleanValue();

    完成上述代码工作后,我们还需要在 Web.xml 中设置好上面创建的两个 Servlet,然后再部署到 Web 服务器上,图 1 是部署成功后的示例 1 首页。

    图 1. 示例 1 的首页
    图 1. 示例 1 的首页

    Sample1Index.jpg

    至此,我们完成了第一个示例,该示例采用了 JCaptcha 提供的默认实现,下面我们将通过扩展使用 JCaptcha,来生成一个更为复杂的图形验证码。

    使用 JCaptcha 生成复杂图形验证码

    在实际项目中,我们可能需要使用更为复杂的图形验证码,比如,加入带干扰的图形背景、更多的干扰点、自定义随机字库及图形字体等等,下面将基于示例 2 介绍如何使用 JCaptcha 生成更为复杂的图形验证码。

    自定义 ImageCaptchaService 的实现

    正 如介绍示例 1 时所提到的,我们可以通过实现 ImageCaptchaService 接口来创建我们自己所需要的 service 对象,清单 5 为示例 2 对接口 ImageCaptchaService 的实现,我们可以在该实现中,使用自己开发的图形验证码生成引擎(SampleListImageCaptchaEngine)来生成更为复杂的图形验证 码。

    清单 5. 示例 2 中 ImageCaptchaService 的实现
     public class SampleImageCaptchaService extends 
        AbstractManageableImageCaptchaService implements ImageCaptchaService { 
    
      private static SampleImageCaptchaService instance; 
    
      public static SampleImageCaptchaService getInstance() { 
        if (instance == null) { 
          //use customized engine 
          ListImageCaptchaEngine engine = new SampleListImageCaptchaEngine(); 
          instance = new SampleImageCaptchaService( 
            new FastHashMapCaptchaStore(), engine, 180, 100000, 75000); 
        } 
        return instance; 
      } 
      
      public SampleImageCaptchaService(CaptchaStore captchaStore, 
          CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds, 
          int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) { 
        super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds, 
            maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection); 
      } 
     }

    构建生成图形验证码的 ImageCaptchaEngine

    清单 5 中的 FastHashMapCaptchaStore 用于存放生成的验证码字符,FastHashMapCaptchaStore 基本可以满足绝大多数项目需求,我们通常不需要创建自己的类来实现它的功能;SampleListImageCaptchaEngine 则是扩展的核心,该类是 ImageCaptchaEngine 的一个实现,在该类中,我们可以设置随机字库、生成的字体和大小、字符数、背景图片以及干扰点等等。清单 6 为 SampleListImageCaptchaEngine 代码片段。

    清单 6. SampleListImageCaptchaEngine 代码片段
     //create text parser 
     TextPaster randomPaster = new DecoratedRandomTextPaster(new Integer(8), 
            new Integer(10), new SingleColorGenerator(Color.BLACK), 
     new TextDecorator[] { new BaffleTextDecorator(new Integer(1), Color.WHITE) }); 
     //create image captcha factory 
     ImageCaptchaFactory factory = new GimpyFactory( 
            new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz"), 
    
    
        new ComposedWordToImage(new TwistedRandomFontGenerator(new Integer(34), 
                new Integer(40)), new FunkyBackgroundGenerator(new Integer( 
                260), new Integer(70)), randomPaster)); 
        
     ImageCaptchaFactory characterFactory[] = { factory}; 
     this.addFactories(characterFactory);

    在清单 6 中,DecoratedRandomTextPaster 的第一个参数用于设置验证码最少字符数,第二个参数为最多的字符数,第三个参数 SingleColorGenerator 为字体颜色,这里为黑色,TextDecorator 为干扰设置,这里是一个字符一个干扰点,并且干扰点为白色。

    在 ImageCaptchaFactory 中,第一个参数设置了随机字库,这里为英文字母,在第二个参数中,TwistedRandomFontGenerator 设置了生成的字符字体,最小 34,最大为 40,FunkyBackgroundGenerator 则用于生成干扰背景,除了设置字体大小外,还需要设置生成的图片大小,示例 2 为 260*70 像素。图 2 为示例 2 的首页截图。

    图 2. 示例 2 的首页
    图 2. 示例 2 的首页

    Sample2Index.jpg

    可 以说 ImageCaptchaEngine 的不同实现,决定了图形验证码的不同样式,JCaptcha 目前提供了多种 ImageCaptchaEngine 的实现,这些实现提供了多种不同的图形验证码样式,当然,我们也可以自己实现 TextPaster 及 TextDecorator 等,进而再继承实现 ImageCaptchaEngine,从而实现自己所期望的效果。图 3 为 JCaptcha 官方网站提供的部分 ImageCaptchaEngine 的实现截图,更多样式,请参见 官方网站

    图 3. JCaptcha 官方图形验证码示例
    图 3. JCaptcha 官方图形验证码示例

    JCaptchaSample.jpg

    开发声音验证码

    由 于某些项目需要支持盲人使用,而盲人无法看到图形验证码,这时,我们就需要同时提供声音版的验证码。JCaptcha 同时支持声音版验证码,但默认实现并不支持生成的声音和图形验证码中的字符一致,这里就需要通过一定的扩展定制,才能保证声音和图形验证码的内容一致,下 面首先介绍如何生成声音验证码,然后介绍如何扩展定制,才能保证声音和图形验证码的内容的一致。

    FreeTTS

    JCaptcha 使用了 FreeTTS 来开发声音验证码,FreeTTS 是一个采用 Java 编写的语音合成项目,它基于 Flite 项目编写,Flite 项目是一个由卡内基梅隆大学开发的小型语音合成引擎,其又源于爱丁堡大学的 Festival 语音合成系统和卡内基梅隆大学的 FestVox 项目。本文的示例使用了 FreeTTS 1.2.2,该版本的有如下几个方面的功能:

    • 一个语音合成引擎
    • 支持多种声音
      • 1 个 8khz,多音位,男性美国英语发音
      • 1 个 16khz,多音位,男性美国英语发音
      • 1 个 16khz 有限声音域的男性美国英语发音
    • 支持从 FestVox 导入声音(仅支持美国英语)
    • 一定程度上支持从 FestVox 中导入 CMU ARCTIC 声音
    • 支持 MBROLA 声音(需另外下载该功能包)
      • 1 个 16khz 女性美国英语发音
      • 2 个 16khz 男性美国英语发音
    • 部分支持 JSAPI 1.0

    为了使用 FreeTTS,JCaptcha 还另外提供了一个 Jar 包用于和 FreeTTS 的集成,所以在运行过程中,除了需要引入 FreeTTS 自带的 Jar 包,还需要包括 JCaptcha 提供的和 FreeTTS 集成的包,在示例 3 和 4 中,使用了如下的包:

    • jcaptcha-extension-sound-freetts-1.0.jar:用于和 FreeTTS 的集成
    • jcaptcha-1.0-all.jar: JCaptcha 核心包
    • freetts.jar:FreeTTS 核心包
    • en_us.jar:用于 FreeTTS
    • commons-logging-1.1.1.jar:用于 JCaptcha
    • commons-collections-3.2.jar:用于 JCaptcha
    • cmutimelex.jar:用于 FreeTTS
    • cmulex.jar:用于 FreeTTS
    • cmudict04.jar:用于 FreeTTS
    • cmu_us_kal.jar:用于 FreeTTS
    • cmu_time_awb.jar:用于 FreeTTS

    构建生成声音的 SoundCaptchaEngine

    同 ImageCaptchaEngine 一样,JCaptcha 同时提供了 SoundCaptchaEngine,所以整个声音验证码的核心是创建 SoundCaptchaEngine 的实现,当然 JCaptcha 也提供了一些默认的实现,比如 DefaultSoundCaptchaEngine 以及 SpellerSoundCaptchaEngine 等等,为了考虑能够支持生成和图形验证码相同的字符,示例 3 采用继承抽象类 ListSoundCaptchaEngine 的方式,来创建自己的实现 SampleListSoundCaptchaEngine。清单 7 为 SampleListSoundCaptchaEngine 代码片段。

    清单 7. SampleListSoundCaptchaEngine 代码片段
     public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine {  
    
      private static String voiceName = "kevin16"; 
    
       private static String voicePackage = 
       "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory"; 
    
      protected void buildInitialFactories() { 
        //create word generator 
      WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
      //create decorator 
     SpellerWordDecorator decorator = new SpellerWordDecorator(", "); 
     //create sound factory 
      SoundCaptchaFactory soundFactory[] = { new SpellerSoundFactory( 
            wordGenerator, 
          new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName, 
              voicePackage, 1.0f, 100, 100), 4, 10), decorator) }; 
        this.setFactories(soundFactory); 
      } 
     }

    创建生成声音验证码的 Servlet

    同开发图形验证码一样,这里也 需要创建 SoundCaptchaService 的实现,实现方式与 ImageCaptchaService 的实现类似,示例 3 的实现类为 SampleSoundCaptchaService,这里不做叙述,读者可以参见附带的示例源代码。

    和 ImageCaptchaService 的实现不同的是,这里不能采用单例的方式来获取 SoundCaptchaService 的实现,否则不能多次为同一 Session ID 生成多个声音验证码文件。另外,在用户不刷新页面,而重复点击声音验证码图标时,我们需要提供同一段声音,因此,我们可以将 SoundCaptchaService 的实现对象以及所生成的字节码流放入到 session 中,清单 7 为 SoundCaptchaServlet 代码片段。

    清单 8. SoundCaptchaServlet 代码片段
     httpServletResponse.setContentType("audio/x-wav"); 
     SoundCaptchaService service = null; 
     if ( httpServletRequest.getSession().getAttribute("soundService") != null) { 
       service = (SoundCaptchaService) httpServletRequest 
          .getSession().getAttribute("soundService"); 
     } else { 
      service = new SampleSoundCaptchaService(); 
     } 
     // get AudioInputStream using session 
     ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();  
     //get from the session if already existed, otherwise, create new one 
     if (httpServletRequest.getSession().getAttribute("stream") == null) { 
          AudioInputStream audioInputStream = service.getSoundChallengeForID( 
      httpServletRequest.getSession().getId(), httpServletRequest.getLocale()); 
    
          AudioSystem.write(audioInputStream, javax.sound.sampled.AudioFileFormat.Type.WAVE, 
                  byteOutputStream); 
     } else { 
        byteOutputStream = 
        (ByteArrayOutputStream)httpServletRequest.getSession().getAttribute("stream"); 
     }    
    
     // output to servlet 
     ServletOutputStream servletOutputStream = httpServletResponse.getOutputStream(); 
     servletOutputStream.write(byteOutputStream.toByteArray()); 
     // save the service object to session, will use it for validation 
     // purpose 
     httpServletRequest.getSession().setAttribute("soundService", service); 
     httpServletRequest.getSession().setAttribute("stream", byteOutputStream); 
     // output to servlet response stream 
     try { 
      servletOutputStream.flush(); 
     } finally { 
      servletOutputStream.close(); 
     }

    创建 Web 页面和验证代码

    为了方便用户使用,我们需要在页面上放 置一个图标,当焦点落在图标上(考虑到盲人可能使用键盘 Tab 键来切换焦点),触发 onFocus 事件来调用 JavaScript 方法显示 embed 标签,从而调用上面生成声音验证码的 Servlet。图 4 为示例 3 首页截图。

    图 4. 示例 3 运行页面截图
    图 4. 示例 3 运行页面截图

    Sample3Index.jpg

    当 声音验证码图标在已经获取焦点,并朗读后,用户可能多次重复让该图标获取焦点以便多次收听,这时,JavaScript 方法会被多次触发,如果不做处理,其将多次请求 Servlet,以致生成不同的声音文件,本示例采用了前一个小节所介绍的缓存 SoundCaptchaService 对象和字节码流的方法来解决这个问题,以保证多次能收听到同一段声音。当然,也可以采用在 URL 后面加一个随机 token 的方式,来判断是否重复提交。清单 8 为示例 3 首页代码片段。

    清单 9. 示例 3 index.jsp 代码片段

     <% 
          
        request.getSession().removeAttribute("soundService"); 
          request.getSession().removeAttribute("stream"); 
     %> 
     <html> 
     <head> 
     <script type="text/javascript"> 
     
     function playSoundCaptcha() { 
       var wavURL = '<%= request.getContextPath() %>'+'/SoundCaptchaServlet'; 
         var embedAudioPlayer = "<EMBED SRC='" + wavURL + "' HIDDEN='true' AUTOSTART='true'  />";  
       var wavArea = document.getElementById("wavArea"); 
       wavArea.innerHTML = embedAudioPlayer; 
     
       
     } 
     </script> 
     </head> 
    
     <form action="CaptchaValidationServlet" method="post"> 
     <table> 
      <tr> 
        <td colspan="2"><img src="ImageCaptchaServlet" /></td> 
      </tr> 
      <tr> 
        <td> 请输入您所看到的字符 :</td> 
        <td><input type="text" name="captcha_input" value="" /> 
        <a href="#"  onFocus="playSoundCaptcha()"> 
      <img src="image/wheelchair.jpg" height="18px" width="18px" 
      alt=""></a><%=request.getAttribute("ERROR") == null ? "" : 
        request  .getAttribute("ERROR")%></td> 
      </tr> 
      <tr> 
        <td><input type="submit" value="提交" /></td> 
      </tr> 
     <div id="wavArea" style="display:none"> 
     </div> 
     </table> 
     </form>

    可以看到,我们采用了不在页面显示的 embed 标签来调用 Servlet 以获取声音流,然后自动播放。在每次页面刷新的时候,还需要将 Session 中的 SoundCaptchaService 的实现对象以及音频流删除,以便页面刷新时,重新生成新的声音验证码。

    后台用来验证用户输入的逻辑和图形版的验证方式相似,这里不做介绍, 具体参见示例源代码。需要注意的是,由于示例 3 生成的声音验证码中的字符和图形版的字符并不一致,在后台验证的逻辑中,只需要用户输入等于声音验证码或图形验证码的字符,即可认为用户输入正确。下面将 介绍如何才能使声音版和图形版中的字符保持一致。

    使声音验证码和图形验证码字符一致

    JCaptcha 并没有提供很直观的方式,来保证声音验证码和图形验证码中的字符一致,这就需要我们了解 JCaptcha 的实现,并能够通过继承的方式来扩展定制 JCaptcha,从而实现声音和图形验证码字符的一致。

    由于图形验证码会先于声音验证码生成,所以,我们第一步就是需要获取图形验证码所生成的字符串,然后是利用所获取的字符串来生成声音验证码。

    获取图形验证码随机数

    在 以上示例中,虽然我们在实现 ImageCaptchaEngine 和 SoundCaptchaEngine 的时候,可以设置随机数生成类,比如 RandomWordGenerator 等,但是即使声音版和图形版采用同一个随机数生成对象,也不能保证会生成同一个字符,因为它们仅仅是设定一个随机字库,而字符则是每次随机生成。因而,我 们并不能通过使用同样的随机数生成对象来生成同样的随机数,只能通过考虑使用图形版生成的字符来生成声音验证码,才能保持两者的一致。

    在创 建 ImageCaptchaEngine 的实现时,我们需要提供一个 ImageCaptchaFactory,实际上,我们可以通过使用继承实现 JCaptcha 已有 ImageCaptchaFactory 的实现,来获取生成的随机数。清单 9 是示例 4 中继承了 GimpyFactory 的代码片段。

    清单 9. SampleGimpyFactory 代码片段
     public class SampleGimpyFactory extends GimpyFactory { 
      
      ……
    
      public ImageCaptcha getImageCaptcha(Locale locale) { 
            //length 
            Integer wordLength = getRandomLength(); 
    
            String word = getWordGenerator().getWord(wordLength, locale); 
            if (this.wordBridge != null) { 
              this.wordBridge.setGeneratedWord(word); 
        } 
            BufferedImage image = null; 
            try { 
                image = getWordToImage().getImage(word); 
            } catch (Throwable e) { 
                throw new CaptchaException(e); 
            } 
    
            ImageCaptcha captcha = 
            new SampleGimpy(CaptchaQuestionHelper.getQuestion(locale, BUNDLE_QUESTION_KEY), 
                    image, word); 
            return captcha; 
      } 
    
     }

    可以发现,通过使用上面的 SampleGimpyFactory 即可获取图形验证码的随机数,所以我们可以将清单 6 中的 GimpyFactory 替换为 SampleGimpyFactory。

    在 获取了生成的随机数后,还需考虑如何将该随机数传递给生成声音验证码的代码。考虑到我们在生成验证码时,都是基于同一个 Session ID 生成,那么我们就可以将生成的随机数放到 map 中,而 key 是 Session ID,那么,在生成验证码字符后,我们就可以用 Session ID 取出该字符串,并放到 Session 中,然后,在生成声音验证码的代码中,就可以从 Session 中获取随机数。但是,并不是所有代码都可以很方便的获取到 ID,所以,我们还需要对示例 2 的代码进行改造,以便能够根据 ID 保存随机数。清单 10 为改造后的清单 5 中的类 SampleImageCaptchaService 的代码片段。

    清单 10. SampleImageCaptchaService 代码片段
     public class SampleImageCaptchaService extends 
        AbstractManageableImageCaptchaService implements ImageCaptchaService { 
    
      … .. 
    
      @Override 
      public BufferedImage getImageChallengeForID(String ID) 
          throws CaptchaServiceException { 
        BufferedImage image=  super.getImageChallengeForID(ID); 
        String generatedWord = ((SampleListImageCaptchaEngine) engine).getWordBridge() 
        .getGeneratedWord(); 
        WordMap.getWordsMap().put(ID, generatedWord); 
        return image; 
      } 
    
     }

    如清单 10 所示,我们将生成的随机数放到了一个 map 中,而 key 则是 ID,这里也就是 SessionID,然后我们就可以在 ImageCaptchaServlet 获取并将该随机数放到 Session 中,如清单 11 所示。接下来便是如何利用该字符生成声音验证码。

    清单 11. ImageCaptchaServlet 代码片段
     httpServletRequest.getSession().setAttribute( 
            "generatedWord", 
            WordMap.getWordsMap().get(httpServletRequest.getSession(true) 
                .getId()));

    利用指定字符生成声音验证码

    为了能够使用指定的字符串生成验证码,我们需要对示例 3 中的代码做一定的修改。这里,示例 4 通过继承 SpellerSoundFactory 实现了一个扩展的 SoundCaptchaFactory,清单 12 为代码片段。

    清单 12. SampleSpellerSoundFactory 代码片段
     public class SampleSpellerSoundFactory extends SpellerSoundFactory { 
    
      private String word; 
      ……
    
      @Override 
      public SoundCaptcha getSoundCaptcha() { 
        return getSoundCaptcha(Locale.getDefault()); 
      } 
    
      @Override 
      public SoundCaptcha getSoundCaptcha(Locale locale) { 
        String soundWord = ""; 
        if (this.word != null && !this.word.equals("")) { 
          soundWord = this.word; 
        } else { 
          soundWord = this.wordGenerator.getWord(getRandomLength(), locale); 
        } 
    
        AudioInputStream sound = this.word2Sound.getSound(wordDecorator 
            .decorateWord(soundWord), locale); 
        SoundCaptcha soundCaptcha = new SpellerSound(getQuestion(locale), 
            sound, word); 
        return soundCaptcha; 
      } 
    
     }

    根据清单 11 中的代码,如果字符串能够正确的传进来,这个 SampleSpellerSoundFactory 将可以根据该字符串生成声音验证码。

    相应的,清单 7 中的 SampleListSoundCaptchaEngine 需要做如清单 13 所示的修改。

    清单 13. SampleListSoundCaptchaEngine 代码
     public class SampleListSoundCaptchaEngine extends ListSoundCaptchaEngine { 
      
      private String word;  
    
      private static String voiceName = "kevin16"; 
    
      private static String voicePackage = 
      "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory";
    
      protected void buildInitialFactories() { 
        WordGenerator wordGenerator = new RandomWordGenerator("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
         SpellerWordDecorator decorator = new SpellerWordDecorator(", "); 
        SoundCaptchaFactory soundFactory[] = { new SampleSpellerSoundFactory( 
            wordGenerator, 
            new FreeTTSWordToSound(new FreeTTSSoundConfigurator(voiceName,
              voicePackage, 1.0f, 100, 100), 4, 10), decorator, word) }; 
        this.setFactories(soundFactory); 
      } 
    
      public SampleListSoundCaptchaEngine(String word) { 
          this.word = word; 
          buildInitialFactories(); 
      
      } 
    
     }

    需要注意的是,我们一定要有如清单 13 所示的带参数的够找函数,否则初始化时,该类会首先调用父类的构造函数,其父类会直接调用 buildInitialFactories 函数,而此时字符串还没有传递给父类。

    接下来需要修改 SampleSoundCaptchaService,在该类中使用 SampleListSoundCaptchaEngine 并传入随机数参数。

    清单 14. SampleSoundCaptchaService 代码片段
     public class SampleSoundCaptchaService extends 
        AbstractManageableSoundCaptchaService implements SoundCaptchaService { 
      
      public SampleSoundCaptchaService(String word) { 
        super(new FastHashMapCaptchaStore(), 
            new SampleListSoundCaptchaEngine(word), 180, 100000, 75000); 
      } 
      ……
    
      
     }

    最后,我们只需要修改 SoundCaptchaServlet,先依据 Session ID 获取生成的随机数,然后调用清单 14 的 SampleSoundCaptchaService 生成声音验证码,如清单 15 所示。

    清单 15. SoundCaptchaServlet 代码片段
     String word = ""; 
     if (httpServletRequest  .getSession().getAttribute("generatedWord") != null) { 
      word = (String)httpServletRequest.getSession().getAttribute("generatedWord"); 
      logger.info("Get generated word from the session, word=" + word); 
     } 
     service = new SampleSoundCaptchaService(word);

    至于后台验证逻辑,可以不做修改,也可以删除原先用于验证声音验证码的代码。至此,声音验证码的字符将与图形验证码的字符保持一致。

    总结

    本 文先从一个简单的示例入手,介绍了如何采用 JCaptcha 开发一个简单的图形验证码,然后介绍了如何通过扩展定制,实现更为复杂的图形验证码,接下来介绍了如何采用 JCaptcha 开发声音验证码,并在最后,介绍了如何扩展 JCaptcha 来保证图形验证码和声音验证码的内容一致。

    下载

    描述 名字 大小
    样例代码 SourceCode.zip 105KB

    参考资料

    学习

    讨论

    展开全文
  • 类似微信,当按下按钮后开始接收信号,接收信号后调用DLL文件,得到一段字符串,进而进行命令词识别,命令词即可操纵机器人模型使之产生动作。开辟缓冲区,从设备接收信号,放大,增加分贝,然后生成*.wav文件。先看...

    类似微信,当按下按钮后开始接收信号,接收信号后调用DLL文件,得到一段字符串,进而进行命令词识别,命令词即可操纵机器人模型使之产生动作。

    开辟缓冲区,从设备接收信号,放大,增加分贝,然后生成*.wav文件。

    先看范例文档范例见labview\examples\Graphics and Sound\Sound

    应该对声音信号进行适当的处理以实现最好的识别效果

    计算机生成了可选文字: (Continuous Sound Input.vi) )  Continuous Samples  -EID  4511:  LzsSR.Iv  01

     

    采样模式

    采样模式指定VI每次采集一个采样(有限采样)或连续采样(连续采样)。 在有限采样模式下,写入每通道采样总数指定的采样数之前,只调用读取声音输入。在连续采集模式下,可根据需要反复调用“读取声音输入”。

    计算机生成了可选文字: (Simultaneous Sound Input Outpa ( LzsSR.lvproj/—ajZhü )  17 pt  Number of Samples/ch  Continuous Samples  44100  É%ÆID  LzsSR.Iv  01  4511:

     

    思路:收集声音和写入文件同时进行,文件写入完毕后,上传和识别同时进行。

    配置麦克风

    调整麦克风的位置,使鼻息不要正对麦克风;

    当麦克风输入声音很小时,可以在windows 上进行设置以增强声音,右击声音图标-录音设备-麦克风-属性-级别-麦克风增强、

    设备ID是声音操作时使用的输入或输出设备。通常,绝大多数情况下都应选择默认值0。 可选值的范围为0到n–1,n是计算机上输入或输出设备的个数。

    设备ID设为0的前提是:在“录音设备管理器”中将“麦克风”设备设为默认设备

    labVIEW状态图大型程序编写

    应该将数据流按功能分类,并且绑定成簇,按规则命名,应该尽量封装函数。

    关于labVIEW编程,顺序结构+循环等待,程序运行失败

    打开文件之后,添加循环等待函数,再进行循环写入文件函数,程序运行失败,显示为缓存错误,该错误可能与状态图中机器人模块的崩溃原因相似。

    计算机生成了可选文字: 수 11  1  1

     

    程序在别的电脑上不能正常运行

    解决办法:须有VS,打开CreateDll 项目,重新生成一次即可,需要设置三项东西:

    配置属性-常规-平台工具集-更改为VS2010V100-解决无法include 问题

    配置属性-生成事件-后期生成事件-命令行-copy 指令目标路径重新指定

    原因分析:推测编译配置由dubug 改为 release 即可


    转载于:https://www.cnblogs.com/lizhensheng/p/11241939.html

    展开全文
  • 邀请机器人 设置Firebase 前往 创建一个新项目 转到项目设置->服务帐户 将databaseUrl添加到环境变量DATABASE_URL 生成一个新密钥并将其转换为环境变量FIREBASE_CONFIG的json 设置Heroku 创建Heroku帐户 将您...
  • 自然语言处理 (NLP) 的最新改进正在通过语音和文本功能来支持主流技术,无论是使用听起来更加自然的声音来大声朗读电子邮件,还是使用Excel输入有关电子表格数据并以自动生成图表和数据透视表的形式来获得答案。...
  • 100个项目构想 主题 报价生成器 待办事项应用 倒计时器 跑表 翻转硬币应用 世界时钟仪表板 早上好松弛机器人 日报机器人 邮件撰写器用户界面 ...离开原因生成器 ...动力机器人 ...PWA-有趣的声音目录 PWA-如
  • gpt3-chatbot-源码

    2021-04-13 04:00:16
    它使用GPT-3生成您的声音,通过您的麦克风听,并通过您的扬声器讲话。 我通过使用GPT-3作为聊天机器人来分享我一直以来的乐趣。 试一试,并调整提示以查看您可以赋予它的其他个性! 安装依赖 创建一个虚拟环境,并...
  • 所以,钉钉提醒声音一响起来,我整个人就不好了~ 那么作为开发人员,怎么让钉钉提醒成为你的噩梦呢? 当然是,让系统报警通过钉钉提醒发给你~这样的话,第一时间能够发现系统问题并合理解决,不影响公司业务发展,...
  • TTS Reader-crx插件

    2021-04-04 03:33:25
    所有其他的TTS Reader或文本语音转换服务主要由TELEPHONE CENTRALS使用,它们听起来完全像机器人一样,绝对没有问题! 这个TTS阅读器是不同的! 我们所有的声音都具有使声音听起来真实的元素,并具有使人们更加专注...
  • NLP( 包括语音识别)

    2021-04-18 14:38:39
    应用:情感分析,聊天机器人(QA), 机器翻译,摘要生成, NLP 与语音识别、语音合成 语音交互: 进行对话交互时,机器往往需要完成“听懂——理解——回答”的闭环。这个闭环涉及到三类技术:语音识别(ASR)、自然...
  • 谷歌大脑和 DeepMind 合作发布 NSynth (Neural Synthesizer)神经声音合成器 @新智元从0到1走进 Kaggle @爱可可-爱生活NLP数据集加载工具集 @爱可可-爱生活Roboschool:用于机器人仿真的开源软件 @网路冷眼...
  • 计划的功能:-炮塔和陷阱-机器人敌人-大大改善了AI-动态难度(基于情节进展,可以使更多生成的敌人具有更好的AI)-进入建筑物时进行武器商店-完成建筑物后显示结果屏幕-库存物品(拆除)套件,地雷,手榴弹,医疗包...
  • 删除烦人的生成声音或全局声音 删除一些全屏效果(此效果不可靠) 超过限制的头像不会被灰色的机器人取代。 取而代之的是,删除超出限制的元素,其余的化身则保持不变。 隐藏特定作者的所有头像(需要...
  • 这不是您的普通聊天机器人-我们的应用程序会生成说话人的同步视听表示,就好像您在现实生活中正在与该人说话一样。 通过将文本和嘴唇合成的语音同步到语音,它可以提供一种体验,使您与与实际不存在的人交谈更近了...
  • 基于声音的室内机器人定位 所有代码和数据集都可以在以下存储库中找到: 存储库包含三个 matlab 文件: 获取RIR.m 数据集.m 数据处理.m GetRIR 是收集数据的辅助函数。 它可以在没有任何参数的情况下在 Matlab 中...
  • shortuuid:一个生成器库,用以生成简洁的,明白的,URL 安全的 UUID。 simplejson:Python 的 JSON 编码、解码器。 unidecode:Unicode 文本的 ASCII 转换形式 。 uniout:打印可读的字符,而不是转义的字符串。...
  • 机器人学 科学计算 脚本 序列化 视频 虚拟机 Web应用框架 XML 多项混杂 软件 编译器 在线编译器 调试器 集成开发环境(IDE) 构建系统 静态代码分析 标准库 C++标准库,包括了STL容器,算法和函数等。 C++ ...
  •  Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

空空如也

空空如也

1 2
收藏数 35
精华内容 14
关键字:

机器人声音生成