godot2d的一些使用技巧(2)
前言吐槽:dfq作者无双说过,不要用冷门引擎。果然,相对于unity来说,godot在细节上面实在是坑爹。“人类从历史中学到的唯一教训,就是人类无法从历史中学到任何教训。——黑格尔”,这个实验项目完成后还是转移去unity算了……
1、windows平台上的导出还需要一个叫rcedit的程序,这个程序藏得很深,在官方的渐进式教程——导出——“在Windows上,如果希望导出的可执行文件的图标与默认图标不同,则需要手动更改它。请参阅: 更改Windows的应用程序图标。”中的“在导出选项中选择它之前,您需要额外安装一个名为 rcedit 的工具。您可以在 这里 下载它。”这个工具的作用是修改windows下exe的图标……而且修改后移动exe还会使exe缩略图标失效变成默认图标……我服了……
2、godot的UI节点默认Rect——min size是(0,0),如果是panel,那么可能会出现panel完全不显示的情况,要修改和size完全一样……这是哪门子设计……
3、godot的碰撞体在instance()后就可以触发碰撞事件,即使它在程序内还是远程debug内都是不可视的……这个东西在instance()的时候大概会位于窗口默认大小的正中间,所以有时候会出现些奇奇怪怪的bug……
4、如果使用信号,就会发现信号需要函数触发,函数需要连接到信号上……于是有如下逻辑:底层机制——信号——函数——信号——函数……,例如:敌人死亡的时候发射信号isdying到敌人出生点,敌人出生点有个执行Timer timerout的函数_timerstart()连接信号isdying,_timerstart()在敌人出生点的ready()函数里连接到敌人出生点的_loadenemy()函数,然后_loadenemy()函数执行新建敌人的部分……
太绕了,上代码:
/// <summary> /// 敌人 /// </summary> public class Enemy : Sprite { public delegate void isdying();//自身死亡信号 public override void _Ready() { player = GetNode<Sprite>("../Player"); RigidBody2D rigidBody2D = this.GetNode<RigidBody2D>("EnemyRigidBody2D"); rigidBody2D.Connect("body_entered", this, nameof(_die));//碰撞信号触发自身死亡 enemyhome = GetNode<Sprite>("../EnemyHome"); } } /// <summary> /// 碰撞销毁 /// </summary> /// <param name="node">被碰撞的碰撞体</param> private void _die(Node node) { if (node.Name == "bulletRigidBody2D") { EmitSignal(nameof(isdying));//发射自身死亡信号 this.QueueFree(); } }
/// <summary> /// 敌人出生点 /// </summary> public class EnemyHome : Sprite { Timer timer;//延时生成敌人的计时器 public override void _Ready() { timer = this.GetNode<Timer>("Timer"); timer.Connect("timeout", this, nameof(timeoutEvent));//添加计时器触发后的执行函数 _loadenemy();//初始化后立即创建一个敌人 } /// <summary> /// 生成敌人 /// </summary> private void _loadenemy() { Node enemy = GD.Load<PackedScene>("res://prefabs/Enemy.tscn").Instance(); RigidBody2D enemyRigidBody2D = enemy.GetNode<RigidBody2D>("EnemyRigidBody2D"); enemy.Connect("isdying", this, nameof(_timerstart)); ((Sprite)enemy).Position = this.Position; enemyRigidBody2D.ContactMonitor = true;//开启碰撞检测 enemyRigidBody2D.ContactsReported = 99;//默认为0,值大于0时碰撞信号才会传入被碰撞体; //初始化并调整位置后才启用碰撞检测 //碰撞体实例化时会默认在窗口正中,即使未显示也能触发检测,所以要在预制体先阻止碰撞检测 GetNode<Node>("../").AddChild(enemy); ((Enemy)enemy).speed += speed; } private void timeoutEvent() { _rebuildEnemy(); } private void _timerstart() { timer.Start(3); } /// <summary> /// 生成新敌人 /// </summary> private void _rebuildEnemy() { _loadenemy(); } }
逻辑如图:
另外想说的是,如果要使用信号机制在多个物体间传导的话,将connect写在ready()里执行会好一点……吧……但这样一来耦合度不也上去了嘛……
3、开启碰撞检测的方法:
预制体要先设置为false和0,instance()后再设置回来
4、朝向:
this.LookAt(player.GlobalPosition);//朝向鼠标 this.RotationDegrees += 90f;//自身朝向 //LookAt()函数使素材的右边朝向坐标位置
5、直接获得根节点:
Node node= this.GetTree().Root.GetChild<Node>(0);
6、限定移动范围:
x: Mathf.Clamp(this.Position.x, minmap.x, maxmap.x), y: Mathf.Clamp(this.Position.y, minmap.y, maxmap.y)
);
7、退出程序和游戏暂停:
1 GetTree().Quit();//退出程序 2 this.GetTree().Paused = false;//游戏暂停,节点设置mode为process则不受影响
8、获取其他节点的脚本的变量:
1 int enemyScore; 2 enemyScore = Convert.ToInt32(node.GetNode<Sprite>("..").Get(nameof(Enemy.Score)));//获取怪物得分值 3 public class Enemy : Sprite 4 { 5 public int Score;//击杀得分 6 }
1 enemyHome.Position = new Vector2 2 ( 3 x:Player.minmap.x-100, 4 y: new Random().Next(Convert.ToInt32(Player.minmap.y), Convert.ToInt32(Player.maxmap.y)) 5 ); 6 public class Player : Sprite 7 { 8 public static Vector2 minmap = new Vector2(100, 100);//角色可移动范围,其一顶点 9 public static Vector2 maxmap = new Vector2(1100, 600);//角色可移动范围,对角顶点 10 }
(完)