首页 > 其他分享 >GameMaker Studio开发:高级动作系统_GML脚本语言的高级运用

GameMaker Studio开发:高级动作系统_GML脚本语言的高级运用

时间:2024-12-15 15:00:03浏览次数:6  
标签:enemy GameMaker 脚本语言 show damage 高级 debug var message

GML脚本语言的高级运用

在上一节中,我们介绍了基本的GML脚本语言及其在GameMaker Studio中的应用。本节将深入探讨GML脚本语言的高级运用,包括函数、变量、控制结构、面向对象编程和性能优化等方面。这些内容将帮助你更好地利用GML编写复杂且高效的代码,从而提升你的游戏开发能力。

1. 高级函数的使用

1.1 函数的定义与调用

函数是编程中非常重要的概念,它们可以将代码模块化,提高代码的可读性和可维护性。在GML中,函数的定义和调用与大多数编程语言相似,但有一些特定的语法和功能。

1.1.1 函数的定义

在GML中,你可以使用function关键字来定义函数。函数可以有参数,也可以有返回值。以下是一个简单的函数定义示例:


// 定义一个简单的函数,用于计算两个数的和

function add_numbers(a, b) {

    // 检查参数类型

    if (is_real(a) && is_real(b)) {

        return a + b; // 返回两个数的和

    } else {

        show_error("参数必须是实数");

        return 0; // 返回0表示错误

    }

}

1.1.2 函数的调用

定义好的函数可以通过函数名和参数来调用。以下是如何调用上述add_numbers函数的示例:


// 调用add_numbers函数

var result = add_numbers(3, 5);

show_debug_message("3 + 5 = " + string(result)); // 输出: 3 + 5 = 8



// 错误调用

result = add_numbers("3", 5);

show_debug_message("结果: " + string(result)); // 输出: 结果: 0

1.2 参数传递

GML中的函数参数可以是各种数据类型,包括实数、字符串、数组、结构体等。参数传递可以是按值传递,也可以是按引用传递。

1.2.1 按值传递

按值传递是指在函数调用时,传递的是参数的副本,因此函数内部对参数的修改不会影响到外部的变量。


// 定义一个函数,按值传递参数

function modify_value(x) {

    x += 10;

}



// 调用函数

var num = 5;

modify_value(num);

show_debug_message("num = " + string(num)); // 输出: num = 5

1.2.2 按引用传递

按引用传递是指在函数调用时,传递的是参数的引用,因此函数内部对参数的修改会影响到外部的变量。在GML中,数组和结构体通常按引用传递。


// 定义一个函数,按引用传递参数

function modify_array(arr) {

    arr[0] += 10;

}



// 调用函数

var numbers = [1, 2, 3];

modify_array(numbers);

show_debug_message("numbers = " + string(numbers)); // 输出: numbers = [11, 2, 3]

1.3 可变参数函数

GML支持定义可变参数的函数,这使得函数可以接受任意数量的参数。使用argument_countargument关键字可以实现这一点。


// 定义一个可变参数函数,用于计算所有参数的和

function sum() {

    var total = 0;

    for (var i = 0; i < argument_count; i++) {

        total += argument[i];

    }

    return total;

}



// 调用函数

var result = sum(1, 2, 3, 4, 5);

show_debug_message("总和 = " + string(result)); // 输出: 总和 = 15

1.4 递归函数

递归函数是指在函数内部调用自身的函数。递归在处理复杂问题时非常有用,例如树结构的遍历、排序算法等。


// 定义一个递归函数,用于计算阶乘

function factorial(n) {

    if (n == 0) {

        return 1;

    } else {

        return n * factorial(n - 1);

    }

}



// 调用函数

var result = factorial(5);

show_debug_message("5! = " + string(result)); // 输出: 5! = 120

2. 高级变量的使用

2.1 变量作用域

在GML中,变量的作用域有全局变量和局部变量之分。全局变量在整个游戏范围内都可见,而局部变量只在定义它们的函数或事件中可见。

2.1.1 全局变量

全局变量可以在任何地方定义和访问。通常,全局变量用于存储游戏的全局状态,例如得分、生命值等。


// 在创建对象事件中定义全局变量

global.score = 0;

global.lives = 3;



// 在其他对象的事件中访问全局变量

show_debug_message("当前得分: " + string(global.score)); // 输出: 当前得分: 0

show_debug_message("当前生命值: " + string(global.lives)); // 输出: 当前生命值: 3

2.1.2 局部变量

局部变量只在定义它们的函数或事件中可见。局部变量可以避免变量命名冲突,提高代码的可读性和可维护性。


// 定义一个带有局部变量的函数

function calculate_damage(attack_power, defense) {

    var damage = attack_power - defense;

    if (damage < 0) {

        damage = 0;

    }

    return damage;

}



// 调用函数

var damage = calculate_damage(50, 20);

show_debug_message("伤害值: " + string(damage)); // 输出: 伤害值: 30

2.2 变量类型

GML支持多种变量类型,包括实数、字符串、数组、结构体等。了解这些变量类型及其使用方法对于编写高效且正确的代码至关重要。

2.2.1 实数和字符串

实数和字符串是最基本的变量类型,它们的使用方式与其他编程语言相似。


// 定义实数和字符串变量

var health = 100.0;

var name = "Player1";



// 输出变量

show_debug_message("玩家名称: " + name); // 输出: 玩家名称: Player1

show_debug_message("玩家生命值: " + string(health)); // 输出: 玩家生命值: 100.0

2.2.2 数组

数组是用于存储多个值的变量。GML中的数组可以是动态数组,也可以是固定大小的数组。


// 定义一个动态数组

var items = ds_list_create();

ds_list_add(items, "剑");

ds_list_add(items, "盾");

ds_list_add(items, "弓");



// 输出数组内容

for (var i = 0; i < ds_list_size(items); i++) {

    show_debug_message("物品 " + string(i) + ": " + ds_list_find_value(items, i));

}



// 销毁数组

ds_list_destroy(items);


// 定义一个固定大小的数组

var numbers = [1, 2, 3, 4, 5];



// 输出数组内容

for (var i = 0; i < array_length_1d(numbers); i++) {

    show_debug_message("数字 " + string(i) + ": " + string(numbers[i]));

}

2.2.3 结构体

GML中的结构体可以用来存储相关的多个值。结构体可以通过点操作符来访问和修改其成员。


// 定义一个结构体

var player = {

    name: "Player1",

    health: 100.0,

    score: 0

};



// 修改结构体成员

player.health -= 10;



// 输出结构体成员

show_debug_message("玩家名称: " + player.name); // 输出: 玩家名称: Player1

show_debug_message("玩家生命值: " + string(player.health)); // 输出: 玩家生命值: 90.0

show_debug_message("玩家得分: " + string(player.score)); // 输出: 玩家得分: 0

2.3 变量的默认值

在GML中,你可以为变量设置默认值,这样在变量未被赋值时,会使用默认值。


// 定义一个带有默认值的函数

function greet(name = "Guest") {

    show_debug_message("欢迎, " + name);

}



// 调用函数

greet("Player1"); // 输出: 欢迎, Player1

greet(); // 输出: 欢迎, Guest

3. 高级控制结构

3.1 条件语句

条件语句用于根据不同的条件执行不同的代码块。GML中的条件语句包括ifelse ifelse


// 定义一个函数,用于判断玩家的生命值

function check_health(health) {

    if (health > 80) {

        show_debug_message("玩家生命值很高");

    } else if (health > 50) {

        show_debug_message("玩家生命值中等");

    } else if (health > 0) {

        show_debug_message("玩家生命值很低");

    } else {

        show_debug_message("玩家已死亡");

    }

}



// 调用函数

check_health(90); // 输出: 玩家生命值很高

check_health(60); // 输出: 玩家生命值中等

check_health(30); // 输出: 玩家生命值很低

check_health(0); // 输出: 玩家已死亡

3.2 循环语句

循环语句用于重复执行某段代码,直到满足特定条件为止。GML中的循环语句包括forwhiledo...while

3.2.1 for循环

for循环是最常用的循环语句,适用于已知循环次数的情况。


// 使用for循环遍历数组

var numbers = [1, 2, 3, 4, 5];

for (var i = 0; i < array_length_1d(numbers); i++) {

    show_debug_message("数字 " + string(i) + ": " + string(numbers[i]));

}

3.2.2 while循环

while循环适用于在满足特定条件时重复执行代码。


// 使用while循环计算阶乘

var n = 5;

var result = 1;

while (n > 0) {

    result *= n;

    n -= 1;

}

show_debug_message("5! = " + string(result)); // 输出: 5! = 120

3.2.3 do...while循环

do...while循环至少会执行一次代码块,然后在每次循环结束时检查条件。


// 使用do...while循环计算阶乘

var n = 5;

var result = 1;

do {

    result *= n;

    n -= 1;

} while (n > 0);

show_debug_message("5! = " + string(result)); // 输出: 5! = 120

3.3 嵌套控制结构

嵌套控制结构是指在一个控制结构内部使用另一个控制结构。这种结构可以处理更复杂的问题。


// 使用嵌套控制结构遍历二维数组

var grid = [

    [1, 2, 3],

    [4, 5, 6],

    [7, 8, 9]

];



for (var i = 0; i < array_length_1d(grid); i++) {

    for (var j = 0; j < array_length_1d(grid[i]); j++) {

        show_debug_message("grid[" + string(i) + "][" + string(j) + "] = " + string(grid[i][j]));

    }

}

4. 面向对象编程

4.1 对象的创建与使用

在GameMaker Studio中,对象是游戏的基本构成单元。每个对象可以有自己的变量、函数和事件。通过合理地创建和使用对象,可以提高游戏的可扩展性和可维护性。

4.1.1 创建对象

在GameMaker Studio中,可以通过创建对象来定义游戏中的角色、敌人、道具等。以下是一个创建玩家对象的示例:

  1. 在对象资源中创建一个名为obj_player的对象。

  2. 在该对象中定义变量和函数。


// 在创建对象事件中定义变量

health = 100.0;

score = 0;



// 定义一个函数,用于计算受到的伤害

function calculate_damage(attack_power, defense) {

    var damage = attack_power - defense;

    if (damage < 0) {

        damage = 0;

    }

    health -= damage;

    return damage;

}

4.1.2 使用对象

在其他对象或脚本中,可以通过对象名来访问和调用对象的变量和函数。


// 在另一个对象中使用obj_player对象

var player = instance_create_layer(x, y, "Instances", obj_player);

player.health -= 10; // 直接修改对象的变量

var damage = player.calculate_damage(50, 20); // 调用对象的函数

show_debug_message("玩家受到的伤害: " + string(damage)); // 输出: 玩家受到的伤害: 30

4.2 继承与多态

GML支持对象继承和多态,这使得你可以创建具有共同特征的对象,并通过继承来扩展这些对象的功能。

4.2.1 对象继承

通过继承,子对象可以继承父对象的变量和函数,并可以重写或添加新的功能。

  1. 创建一个名为obj_enemy的父对象。

  2. 创建一个名为obj_goblin的子对象,继承自obj_enemy


// 在obj_enemy对象中定义变量和函数

health = 50.0;

score = 0;



function calculate_damage(attack_power, defense) {

    var damage = attack_power - defense;

    if (damage < 0) {

        damage = 0;

    }

    health -= damage;

    return damage;

}


// 在obj_goblin对象中重写calculate_damage函数

function calculate_damage(attack_power, defense) {

    var damage = super.calculate_damage(attack_power, defense); // 调用父对象的函数

    if (damage > 0) {

        show_debug_message("哥布林受到了伤害: " + string(damage));

    }

    return damage;

}

4.2.2 多态

多态是指子对象可以替代父对象,调用相同的方法时会执行子对象中重写的方法。


// 在另一个对象中使用多态

var enemy = instance_create_layer(x, y, "Instances", obj_enemy);

var goblin = instance_create_layer(x, y, "Instances", obj_goblin);



var damage1 = enemy.calculate_damage(30, 10);

var damage2 = goblin.calculate_damage(30, 10);



show_debug_message("普通敌人受到的伤害: " + string(damage1)); // 输出: 普通敌人受到的伤害: 20

show_debug_message("哥布林受到的伤害: " + string(damage2)); // 输出: 哥布林受到了伤害: 20

5. 性能优化

5.1 减少冗余计算

在游戏开发中,减少冗余计算可以显著提升性能。例如,可以在需要时才计算某些值,而不是在每个帧中都计算。


// 定义一个变量,用于存储最后一次计算的时间

var last_calculation_time = 0;



// 在步进事件中判断是否需要重新计算

if (current_time - last_calculation_time > 1000) { // 每秒计算一次

    last_calculation_time = current_time;

    var result = complex_calculation();

    show_debug_message("计算结果: " + string(result));

}



function complex_calculation() {

    // 模拟一个复杂的计算过程

    var result = 0;

    for (var i = 0; i < 1000000; i++) {

        result += i;

    }

    return result;

}

5.2 使用缓存

缓存是指将计算结果存储起来,以便在需要时直接使用,而不是重复计算。这可以显著提升性能。


// 定义一个缓存变量

var cached_result = -1;



// 在步进事件中判断是否需要重新计算

if (cached_result == -1) {

    cached_result = complex_calculation();

    show_debug_message("计算结果: " + string(cached_result));

}



function complex_calculation() {

    // 模拟一个复杂的计算过程

    var result = 0;

    for (var i = 0; i < 1000000; i++) {

        result += i;

    }

    return result;

}

5.3 优化数组操作

数组操作是游戏开发中常见的性能瓶颈。合理地使用数组可以提高游戏的性能。

5.3.1 使用动态数组

动态数组可以在运行时动态地增加或减少大小。


// 使用ds_list作为动态数组

var items = ds_list_create();

ds_list_add(items, "剑");

ds_list_add(items, "盾");

ds_list_add(items, "弓");



// 输出动态数组内容

for (var i = 0; i < ds_list_size(items); i++) {

    show_debug_message("物品 " + string(i) + ": " + ds_list_find_value(items, i));

}



// 销毁动态数组

ds_list_destroy(items);

5.3.2#### 5.3.2 使用固定大小的数组

固定大小的数组在内存分配上更高效,因为它们在创建时就分配了固定大小的内存。如果你在游戏开发中知道数组的大小不会改变,使用固定大小的数组是一个更好的选择。


// 定义一个固定大小的数组

var numbers = [1, 2, 3, 4, 5];



// 输出固定大小的数组内容

for (var i = 0; i < array_length_1d(numbers); i++) {

    show_debug_message("数字 " + string(i) + ": " + string(numbers[i]));

}

5.4 避免频繁的对象实例创建和销毁

频繁创建和销毁对象实例会导致性能下降。尽量在游戏开始时一次性创建所有需要的对象,并在游戏结束时统一销毁。

5.4.1 预创建对象

在游戏的初始化阶段,一次性创建所有需要的对象实例,并将它们存储在一个数组或列表中。


// 在创建对象事件中预创建对象实例

var enemies = ds_list_create();

for (var i = 0; i < 10; i++) {

    var enemy = instance_create_layer(100 + i * 50, 100, "Instances", obj_enemy);

    ds_list_add(enemies, enemy);

}



// 在其他事件中使用这些对象实例

for (var i = 0; i < ds_list_size(enemies); i++) {

    var enemy = ds_list_find_value(enemies, i);

    enemy.health -= 10;

}

5.4.2 对象池

对象池是一种常见的性能优化技术,通过重用对象实例来避免频繁的创建和销毁。


// 定义一个对象池

var enemy_pool = ds_list_create();



// 在创建对象事件中初始化对象池

for (var i = 0; i < 10; i++) {

    var enemy = instance_create_layer(-100, -100, "Instances", obj_enemy);

    ds_list_add(enemy_pool, enemy);

}



// 在需要时从对象池中获取对象

function get_enemy() {

    if (ds_list_size(enemy_pool) > 0) {

        var enemy = ds_list_find_value(enemy_pool, 0);

        ds_list_delete(enemy_pool, 0);

        enemy.visible = true;

        enemy.x = 100;

        enemy.y = 100;

        return enemy;

    } else {

        show_error("对象池已空");

        return noone;

    }

}



// 在不再需要时将对象返回到对象池

function return_enemy(enemy) {

    if (enemy != noone) {

        enemy.visible = false;

        ds_list_add(enemy_pool, enemy);

    }

}



// 使用对象池

var enemy = get_enemy();

if (enemy != noone) {

    enemy.health -= 10;

    return_enemy(enemy);

}

5.5 优化图形和碰撞检测

图形和碰撞检测是游戏开发中的重要部分,但也是性能瓶颈。合理地优化这些操作可以显著提升游戏性能。

5.5.1 使用碰撞对象

使用碰撞对象(如instance_placeinstance_nearest等)可以减少碰撞检测的复杂度。


// 使用instance_nearest函数检测最近的敌人

var nearest_enemy = instance_nearest(x, y, obj_enemy);

if (nearest_enemy != noone) {

    show_debug_message("最近的敌人: " + nearest_enemy.name);

}

5.5.2 减少图形渲染

减少不必要的图形渲染可以提升性能。例如,可以只在对象可见时才进行渲染。


// 在绘制事件中检查对象是否可见

if (visible) {

    draw_self();

}

5.6 使用脚本和函数

合理地使用脚本和函数可以提高代码的可读性和可维护性,同时也可以优化性能。将常用的代码段封装成函数或脚本,可以避免重复代码,减少错误。


// 定义一个脚本,用于计算伤害

script_add(script_create("calculate_damage", "function calculate_damage(attack_power, defense) { var damage = attack_power - defense; if (damage < 0) { damage = 0; } return damage; }"));



// 调用脚本

var damage = calculate_damage(50, 20);

show_debug_message("伤害值: " + string(damage)); // 输出: 伤害值: 30

6. 高级调试技巧

6.1 使用调试输出

调试输出是开发过程中非常有用的工具,可以帮助你了解代码的执行情况。GML提供了show_debug_message函数用于输出调试信息。


// 在步进事件中输出调试信息

show_debug_message("当前帧: " + string(current_time));

show_debug_message("玩家位置: (" + string(x) + ", " + string(y) + ")");

6.2 使用断点

断点是调试代码时的重要工具,可以在特定的代码行暂停执行,以便检查变量的值和程序的状态。

  1. 在GameMaker Studio中,右击代码行,选择“插入断点”。

  2. 运行游戏时,程序会在断点处暂停,你可以检查变量的值和调用栈。

6.3 使用性能分析工具

GameMaker Studio提供了性能分析工具,可以帮助你识别代码中的性能瓶颈。通过分析工具,你可以了解哪些函数或代码段消耗了较多的时间,从而进行优化。

  1. 在GameMaker Studio中,选择“工具” > “性能分析器”。

  2. 运行游戏并观察性能分析结果。

7. 总结

通过本节的学习,你已经掌握了GML脚本语言的高级运用,包括函数的定义与调用、参数传递、变量的高级使用、控制结构、面向对象编程和性能优化等方面。这些知识将帮助你在GameMaker Studio中编写更复杂、更高效的游戏代码,提升你的游戏开发能力。

希望这些内容对你有所帮助,祝你在游戏开发的道路上越走越远!


如果你有任何疑问或需要进一步的帮助,请随时提问。
在这里插入图片描述

标签:enemy,GameMaker,脚本语言,show,damage,高级,debug,var,message
From: https://blog.csdn.net/chenlz2007/article/details/144436763

相关文章

  • GameMaker Studio开发:高级动作系统_案例研究:高级动作系统在不同类型游戏中的应用
    案例研究:高级动作系统在不同类型游戏中的应用在上一节中,我们探讨了如何在GameMakerStudio中构建基础的动作系统。本节将通过具体案例研究,展示高级动作系统在不同类型游戏中的应用。这些案例将涵盖常见的动作游戏类型,如平台游戏、射击游戏、格斗游戏和冒险游戏,帮助你更深入......
  • GameMaker Studio开发:高级动作系统_敌人AI与行为模式设计
    敌人AI与行为模式设计在动作游戏中,敌人AI的设计和实现是游戏体验的关键因素之一。一个精心设计的敌人AI可以让玩家感到挑战,增加游戏的趣味性和可玩性。本节将详细介绍如何在GameMakerStudio中设计和实现高级敌人AI与行为模式。敌人AI的设计原则设计敌人AI时,需要考虑以下......
  • GameMaker Studio开发:高级动作系统_动作事件的高级应用:触发与响应
    动作事件的高级应用:触发与响应在上一节中,我们讨论了如何在GameMakerStudio中设置基本的动作事件。这一节,我们将深入探讨如何利用这些事件进行更高级的触发与响应机制,以实现更复杂的游戏逻辑。通过学习本节内容,您将能够:理解不同类型的事件及其触发条件。掌握如何在事件......
  • 人工智能与大数据:迈向专业应用的高级教程
    在掌握了机器学习、深度学习及大数据处理的基础知识后,你可能希望进一步探索更复杂、更贴近真实场景的应用。本教程将带领你学习更加专业的技术与工具,包括高级深度学习技术、强化学习、分布式深度学习,以及大数据生态系统中的实时数据处理与工程化实践。第一部分:深度学习高级......
  • Hive高级查询
    Hive高级查询更多大数据资源持续更新中。。。一、UDTF之explode函数1、explode语法功能对于UDTF表生成函数,很多人难以理解什么叫做输入一行,输出多行。为什么叫做表生成?能够产生表吗?下面我们就来学习Hive当做内置的一个非常著名的UDTF函数,名字叫做explode函数,中文戏称之......
  • Cesium高级开发教程之四:鹰眼地图#OpenLayers
    教程示例网站:https://thomaz529.github.io一、效果图二、代码init2DDiv(){this.mapDiv=document.createElement('div');this.mapDiv.setAttribute('id',this.mapId)constviewerContainer=this.viewer.cesiumWidget.container.pa......
  • 你认为高级前端工程师应该具备哪些技能?
    高级前端工程师是前端开发团队中的关键成员,他们不仅需要有深厚的技术功底,还需要具备良好的架构设计能力、团队协作能力和持续学习的热情。以下是我认为高级前端工程师应该具备的技能:精通前端技术栈:熟练掌握HTML5、CSS3、JavaScript等前端基础技术,能够编写高质量的代码。熟悉......
  • python面向对象高级编程:使用元类
    在Python中,元类(Metaclass)是创建类的“类”。换句话说,元类是用来控制类的行为的。虽然元类在Python中不常用,但在某些高级编程场景中,它们可以提供强大的功能,如自动注册类、验证类定义、修改类属性等。1.导入必要的模块虽然元类不需要导入额外的模块,但你需要了解如何使用内置的......
  • python面向对象高级编程:使用枚举类
    在Python中,枚举类(Enum)是一种特殊的数据类型,它允许我们定义一组命名的常量。使用枚举类可以使代码更加清晰和易于维护,特别是在处理一组相关常量时。Python的enum模块提供了创建枚举类的功能。以下是如何在Python中使用枚举类的一些高级编程技巧:1.导入enum模块首先,我们需要导......
  • Flutter从入门到高级进阶
    Flutter从入门到高级进阶https://www.bilibili.com/video/BV19x4y1R7LEP1环境搭建P2创建Flutter工程&Flutter优势flutter2.5.3appdart代码module混合开发plugin第三包原生和dartpackage第三包dartname下划线Flutter:效率高!!不依赖UI!!高度统一!!渲染引擎—》Dart......