在上一篇《Unity 4.3 制作一个2D横版射击游戏》时,已经完成一个基本的射击游戏了。在这一篇将继续完善。

1.视差卷轴效果

为了达到这种视差卷轴的效果,可以让背景层以不同的速度进行移动,越远的层,移动地越慢。如果操作得当,这可以造成深度的错觉,这将很酷,又是可以容易做到的效果。在这里存在两个滚动:
  • 主角随着摄像机向前推进
  • 背景元素除了摄像机的移动外,又以不同的速度移动
一个循环的背景将在水平滚动的时候,一遍又一遍的重复进行显示。现在添加一个新的层到场景中,来显示背景元素。现有的层如下:

Layer Loop Position   
Background with the sky Yes (0, 0, 10)
Background (1st row of flying platforms) No (0, 0, 9)
Middleground (2nd row of flying platforms) No (0, 0, 5)
Foreground with players and enemies No (0, 0, 0)

如下图所示:

接下来,先实现下不带背景循环的滚动。创建一个脚本,命名为"ScrollingScript",代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using UnityEngine;

/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{
  /// <summary>
  /// Scrolling speed
  /// </summary>
  public Vector2 speed = new Vector2(2, 2);

/// <summary>
  /// Moving direction
  /// </summary>
  public Vector2 direction = new Vector2(-1, 0);

/// <summary>
  /// Movement should be applied to camera
  /// </summary>
  public bool isLinkedToCamera = false;

void Update()
  {
    // Movement
    Vector3 movement = new Vector3(
      speed.x * direction.x,
      speed.y * direction.y,
      0);

movement *= Time.deltaTime;
    transform.Translate(movement);

// Move the camera
    if (isLinkedToCamera)
    {
      Camera.main.transform.Translate(movement);
    }
  }
}

将这个脚本附加到以下对象上,并且设置好其属性值,如下:

Layer Speed Direction Linked to Camera
0 - Background (1, 1) (-1, 0, 0) No
1 - Background elements (1.5, 1.5) (-1, 0, 0) No
2 - Middleground (2.5, 2.5) (-1, 0, 0) No
3 - Foreground (1, 1) (1, 0, 0) Yes
现在添加更多的元素到场景上:
  • 添加两个天空背景到0 - Background
  • 添加一些小的平台到1 - Background elements
  • 添加一些平台到2 - Middleground
  • 添加更多的敌人到3 - Foreground,放置在摄像机的右边
接下来,实现无限背景。当左侧的背景对象远离了摄像机的左边缘,那么就将它移到右侧去,一直这样无限循环,如下图所示:

要做到检查的对象渲染器是否在摄像机的可见范围内,需要一个类扩展。创建一个C#文件,命名为"RendererExtensions.cs",代码如下:
1
2
3
4
5
6
7
8
9
10
using UnityEngine;

public static class RendererExtensions
{
  public static bool IsVisibleFrom(this Renderer renderer, Camera camera)
  {
    Plane[] planes = GeometryUtility.CalculateFrustumPlanes(camera);
    return GeometryUtility.TestPlanesAABB(planes, renderer.bounds);
  }
}

修改"ScrollingScript"脚本文件,为如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

/// <summary>
/// Parallax scrolling script that should be assigned to a layer
/// </summary>
public class ScrollingScript : MonoBehaviour
{
  /// <summary>
  /// Scrolling speed
  /// </summary>
  public Vector2 speed = new Vector2(10, 10);

/// <summary>
  /// Moving direction
  /// </summary>
  public Vector2 direction = new Vector2(-1, 0);

/// <summary>
  /// Movement should be applied to camera
  /// </summary>
  public bool isLinkedToCamera = false;

/// <summary>
  /// 1 - Background is infinite
  /// </summary>
  public bool isLooping = false;

/// <summary>
  /// 2 - List of children with a renderer.
  /// </summary>
  private List<Transform> backgroundPart;

// 3 - Get all the children
  void Start()
  {
    // For infinite background only
    if (isLooping)
    {
      // Get all the children of the layer with a renderer
      backgroundPart = new List<Transform>();

for (int i = 0; i < transform.childCount; i++)
      {
        Transform child = transform.GetChild(i);

// Add only the visible children
        if (child.renderer != null)
        {
          backgroundPart.Add(child);
        }
      }

// Sort by position.
      // Note: Get the children from left to right.
      // We would need to add a few conditions to handle
      // all the possible scrolling directions.
      backgroundPart = backgroundPart.OrderBy(
        t => t.position.x
      ).ToList();
    }
  }

void Update()
  {
    // Movement
    Vector3 movement = new Vector3(
      speed.x * direction.x,
      speed.y * direction.y,
      0);

movement *= Time.deltaTime;
    transform.Translate(movement);

// Move the camera
    if (isLinkedToCamera)
    {
      Camera.main.transform.Translate(movement);
    }

// 4 - Loop
    if (isLooping)
    {
      // Get the first object.
      // The list is ordered from left (x position) to right.
      Transform firstChild = backgroundPart.FirstOrDefault();

if (firstChild != null)
      {
        // Check if the child is already (partly) before the camera.
        // We test the position first because the IsVisibleFrom
        // method is a bit heavier to execute.
        if (firstChild.position.x < Camera.main.transform.position.x)
        {
          // If the child is already on the left of the camera,
          // we test if it's completely outside and needs to be
          // recycled.
          if (firstChild.renderer.IsVisibleFrom(Camera.main) == false)
          {
            // Get the last child position.
            Transform lastChild = backgroundPart.LastOrDefault();
            Vector3 lastPosition = lastChild.transform.position;
            Vector3 lastSize = (lastChild.renderer.bounds.max - lastChild.renderer.bounds.min);

// Set the position of the recyled one to be AFTER
            // the last child.
            // Note: Only work for horizontal scrolling currently.
            firstChild.position = new Vector3(lastPosition.x + lastSize.x, firstChild.position.y, firstChild.position.z);

// Set the recycled child to the last position
            // of the backgroundPart list.
            backgroundPart.Remove(firstChild);
            backgroundPart.Add(firstChild);
          }
        }
      }
    }
  }
}

Start方法里,使用了LINQ将它们按X轴进行排序。将"0 - Background"对象里的"ScrollingScript"组件的"Is Looping"属性勾选,现在运行,就可以看到视差卷轴的效果,如下图所示:

接下来,修改敌人脚本,让敌人静止不动,且无敌,直到摄像机看到它们。另外,当它们移出屏幕时,则立刻移除它们。修改"EnemyScript"脚本,代码为如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
using UnityEngine;

/// <summary>
/// Enemy generic behavior
/// </summary>
public class EnemyScript : MonoBehaviour
{
  private bool hasSpawn;
  private MoveScript moveScript;
  private WeaponScript[] weapons;

void Awake()
  {
    // Retrieve the weapon only once
    weapons = GetComponentsInChildren<WeaponScript>();

// Retrieve scripts to disable when not spawn
    moveScript = GetComponent<MoveScript>();
  }

// 1 - Disable everything
  void Start()
  {
    hasSpawn = false;

// Disable everything
    // -- collider
    collider2D.enabled = false;
    // -- Moving
    moveScript.enabled = false;
    // -- Shooting
    foreach (WeaponScript weapon in weapons)
    {
      weapon.enabled = false;
    }
  }

void Update()
  {
    // 2 - Check if the enemy has spawned.
    if (hasSpawn == false)
    {
      if (renderer.IsVisibleFrom(Camera.main))
      {
        Spawn();
      }
    }
    else
    {
      // Auto-fire
      foreach (WeaponScript weapon in weapons)
      {
        if (weapon != null && weapon.enabled && weapon.CanAttack)
        {
          weapon.Attack(true);
        }
      }

// 4 - Out of the camera ? Destroy the game object.
      if (renderer.IsVisibleFrom(Camera.main) == false)
      {
        Destroy(gameObject);
      }
    }
  }

// 3 - Activate itself.
  private void Spawn()
  {
    hasSpawn = true;

// Enable everything
    // -- Collider
    collider2D.enabled = true;
    // -- Moving
    moveScript.enabled = true;
    // -- Shooting
    foreach (WeaponScript weapon in weapons)
    {
      weapon.enabled = true;
    }
  }
}

现在运行的话,会发现主角永远看不到敌人,因为敌人也跟随者层移动了。解决方法,只需将"3 - Foreground"层上的"ScrollingScript"脚本移除掉即可。在游戏过程中,可以发现主角并不是限制在摄像机区域内的,可以随意离开摄像机,现在来修复这个问题。打开"PlayerScript"脚本,在Update方法里面添加如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void Update()
  {
    // ...

// 6 - Make sure we are not outside the camera bounds
    var dist = (transform.position - Camera.main.transform.position).z;

var leftBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 0, dist)
    ).x;

var rightBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(1, 0, dist)
    ).x;

var topBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 0, dist)
    ).y;

var bottomBorder = Camera.main.ViewportToWorldPoint(
      new Vector3(0, 1, dist)
    ).y;

transform.position = new Vector3(
      Mathf.Clamp(transform.position.x, leftBorder, rightBorder),
      Mathf.Clamp(transform.position.y, topBorder, bottomBorder),
      transform.position.z
    );

// End of the update method
  }

2.粒子效果

制作一个爆炸的粒子,用于敌人或者主角被摧毁时进行显示。创建一个"Particle System",导入烟图片到"Textures"文件夹,改变其"Texture Type"为"Texture",并且勾选"Alpha Is Transparent"属性,附加这个纹理到粒子上,将其拖动到粒子对象上,更改其Shader为"Particles"→"Alpha Blended",接着更改一些属性,如下所示:

Category Parameter name Value
General Duration 1
General Max Particles 15
General Start Lifetime 1
General Start Color Gray
General Start Speed 3
General Start Size 2
Emission Bursts 0 : 15
Shape Shape Sphere
Color Over Lifetime Color See below (N°1)
Size Over Lifetime Size See below (N°2)
其中Color Over Lifetime要设置成在结束时,有个淡出的效果,如下图所示:

Size Over Lifetime选择一个递减曲线,如下图所示:

当调整完成后,取消勾选"Looping",现在粒子效果为如下:

保存成预制,命名为"SmokeEffect",放在"Prefabs/Particles"文件夹下。现在创建另一个粒子,火焰效果,使用默认材质即可。其他设置如下:

Category Parameter name Value
General Looping false
General Duration 1
General Max Particles 10
General Start Lifetime 1
General Start Speed 0.5
General Start Size 2
Emission Bursts 0 : 10
Shape Shape Box
Color Over Lifetime Color See below (N°1)

其中Color Over Lifetime要设置成有一个黄色到橙色的渐变,最后淡出,如下图所示:

粒子效果为:

保存成预制,命名为"FireEffect"。创建一个脚本,命名为"SpecialEffectsHelper",代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using UnityEngine;

/// <summary>
/// Creating instance of particles from code with no effort
/// </summary>
public class SpecialEffectsHelper : MonoBehaviour
{
  /// <summary>
  /// Singleton
  /// </summary>
  public static SpecialEffectsHelper Instance;

public ParticleSystem smokeEffect;
  public ParticleSystem fireEffect;

void Awake()
  {
    // Register the singleton
    if (Instance != null)
    {
      Debug.LogError("Multiple instances of SpecialEffectsHelper!");
    }

Instance = this;
  }

/// <summary>
  /// Create an explosion at the given location
  /// </summary>
  /// <param name="position"></param>
  public void Explosion(Vector3 position)
  {
    // Smoke on the water
    instantiate(smokeEffect, position);

// Tu tu tu, tu tu tudu

// Fire in the sky
    instantiate(fireEffect, position);
  }

/// <summary>
  /// Instantiate a Particle system from prefab
  /// </summary>
  /// <param name="prefab"></param>
  /// <returns></returns>
  private ParticleSystem instantiate(ParticleSystem prefab, Vector3 position)
  {
    ParticleSystem newParticleSystem = Instantiate(
      prefab,
      position,
      Quaternion.identity
    ) as ParticleSystem;

// Make sure it will be destroyed
    Destroy(
      newParticleSystem.gameObject,
      newParticleSystem.startLifetime
    );

return newParticleSystem;
  }
}

这里创建了一个单例,可以让任何地方都可以产生烟和火焰的粒子。将这个脚本附加到"Scripts"对象,设置其属性"Smoke Effect"和"Fire Effect"为对应的预制体。现在是时候调用这个脚本了,打开"HealthScript"脚本文件,在OnTriggerEnter方法里面,更新成如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Damage(int damageCount)
  {
    // ...

if (hp <= 0)
    {
      // 'Splosion!
      SpecialEffectsHelper.Instance.Explosion(transform.position);

// Dead!
      Destroy(gameObject);
    }

// ...
  }

现在运行,射击敌人,可以看到如下效果:

3.音乐和音效

现在来添加一些声音。将声音资源放入"Sounds"文件夹,取消勾选每一个声音的"3D sound"属性,因为这是2D游戏。准备播放背景音乐,创建一个游戏对象,命名为"Music",其Position为(0, 0, 0),将背景音乐拖到这个对象上,然后勾选"Mute"属性。因为音效总是在一定的时机进行播放,所以创建一个脚本文件,命名为"SoundEffectsHelper",代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using UnityEngine;
using System.Collections;

/// <summary>
/// Creating instance of sounds from code with no effort
/// </summary>
public class SoundEffectsHelper : MonoBehaviour
{

/// <summary>
  /// Singleton
  /// </summary>
  public static SoundEffectsHelper Instance;

public AudioClip explosionSound;
  public AudioClip playerShotSound;
  public AudioClip enemyShotSound;

void Awake()
  {
    // Register the singleton
    if (Instance != null)
    {
      Debug.LogError("Multiple instances of SoundEffectsHelper!");
    }
    Instance = this;
  }

public void MakeExplosionSound()
  {
    MakeSound(explosionSound);
  }

public void MakePlayerShotSound()
  {
    MakeSound(playerShotSound);
  }

public void MakeEnemyShotSound()
  {
    MakeSound(enemyShotSound);
  }

/// <summary>
  /// Play a given sound
  /// </summary>
  /// <param name="originalClip"></param>
  private void MakeSound(AudioClip originalClip)
  {
    // As it is not 3D audio clip, position doesn't matter.
    AudioSource.PlayClipAtPoint(originalClip, transform.position);
  }
}

将此脚本附加到"Scripts"对象上,然后设置其属性值,如下图所示:

接着,在"HealthScript"脚本文件里,播放粒子效果后面,添加代码:
1
SoundEffectsHelper.Instance.MakeExplosionSound();

在"PlayerScript"脚本文件里,代码weapon.Attack(false);后面,添加代码:
1
SoundEffectsHelper.Instance.MakePlayerShotSound();
在"EnemyScript"脚本文件里,代码weapon.Attack(false);后面,添加代码:
1
SoundEffectsHelper.Instance.MakeEnemyShotSound();
现在运行,就可以听到声音了。

4.菜单

创建简单的菜单,以便游戏可以重新开始。导入背景图片和LOGO图片到"Textures"文件夹的子文件夹"Menu"。创建一个新的场景,命名为"Menu"。添加背景Sprite对象,其Position为(0, 0, 1),Size为(2, 2, 1)。添加LOGO的Sprite对象,其Position为(0, 2, 0),Size为(0.75, 0.75, 1)。添加一个空对象,命名为"Scripts",用来加载脚本。现在为这个启动画面,添加一个开始按钮。创建一个脚本文件,命名为"MenuScript",代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;

/// <summary>
/// Title screen script
/// </summary>
public class MenuScript : MonoBehaviour
{
  void OnGUI()
  {
    const int buttonWidth = 84;
    const int buttonHeight = 60;

// Draw a button to start the game
    if (
      GUI.Button(
        // Center in X, 2/3 of the height in Y
        new Rect(
          Screen.width / 2 - (buttonWidth / 2),
          (2 * Screen.height / 3) - (buttonHeight / 2),
          buttonWidth,
          buttonHeight
        ),
        "Start!"
      )
    )
    {
      // On Click, load the first level.
      // "Stage1" is the name of the first scene we created.
      Application.LoadLevel("Stage1");
    }
  }
}

将此脚本附加到"Scripts"对象上。现在运行,可以看到如下效果:
但是,点击按钮会崩溃,因为没有将Stage1场景添加进来。打开"File"→"Build Settings",将场景"Menu"和"Stage1"拖动到上面的"Scenes In Build"里面。再次运行,就可以看到按钮正常切换场景了。当主角被摧毁时,需要可以重新开始游戏。创建一个脚本文件,命名为"GameOverScript",代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using UnityEngine;

/// <summary>
/// Start or quit the game
/// </summary>
public class GameOverScript : MonoBehaviour
{
  void OnGUI()
  {
    const int buttonWidth = 120;
    const int buttonHeight = 60;

if (
      GUI.Button(
        // Center in X, 1/3 of the height in Y
        new Rect(
          Screen.width / 2 - (buttonWidth / 2),
          (1 * Screen.height / 3) - (buttonHeight / 2),
          buttonWidth,
          buttonHeight
        ),
        "Retry!"
      )
    )
    {
      // Reload the level
      Application.LoadLevel("Stage1");
    }

if (
      GUI.Button(
        // Center in X, 2/3 of the height in Y
        new Rect(
          Screen.width / 2 - (buttonWidth / 2),
          (2 * Screen.height / 3) - (buttonHeight / 2),
          buttonWidth,
          buttonHeight
        ),
        "Back to menu"
      )
    )
    {
      // Reload the level
      Application.LoadLevel("Menu");
    }
  }
}

在主角死亡的时候,调用这个脚本。打开"PlayerScript"文件,添加如下代码:

1
2
3
4
5
6
7
void OnDestroy()
{
  // Game Over.
  // Add the script to the parent because the current game
  // object is likely going to be destroyed immediately.
  transform.parent.gameObject.AddComponent<GameOverScript>();
}

现在运行,当死亡时,就会出现按钮,如下图所示:

本例子源代码附加资源下载地址:http://download.csdn.net/detail/akof1314/7166535
如文章存在错误之处,欢迎指出,以便改正。转载请注明出处。

Unity 4.3 制作一个2D横版射击游戏(2)相关推荐

  1. Unity 4 3 制作一个2D横版射击游戏 2

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 在上一篇 ...

  2. Unity 4 3 制作一个2D横版射击游戏

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 这是一个 ...

  3. Unity 4.3 制作一个2D横版射击游戏

    这是一个横版射击游戏. 教程来自: http://pixelnest.io/tutorials/2d-game-unity/   中文版教程: http://www.litpo.com/categor ...

  4. Unity3D开发一个2D横版射击游戏

    教程基于http://pixelnest.io/tutorials/2d-game-unity/ , 这个例子感觉还是比较经典的, 网上转载的也比较多. 刚好最近也在学习U3D, 做的过程中自己又修改 ...

  5. Unity学习笔记3 简易2D横版RPG游戏制作(三)

    这一篇本来应该是在上一篇后面直接补进去的.不过因为排版的问题.所以我就另开一篇来整理了,好了,废话不多说,马上整理: 十八.关卡的锁定与解锁 前面我们已经解决了在游戏开始时可以选择关卡的问题,接下来我 ...

  6. unity3d游戏2d横版射击游戏完整项目源码分享

    unity3d游戏2d横版射击游戏完整项目源码分享 免费下载地址: 链接:https://pan.baidu.com/s/1YwhEy7DeKIHFU8pBLdJFPg 提取码:3wnx 复制这段内容 ...

  7. Unity学习笔记1 简易2D横版RPG游戏制作(一)

    这个教程是参考一个YouTube上面的教程做的,原作者的教程做得比较简单,我先参考着做一遍,毕竟我也只是个初学者,还没办法完全自制哈哈.不过我之前也看过一个2D平台游戏的系列教程了,以后会整合起来,做 ...

  8. Unity学习笔记2 简易2D横版RPG游戏制作(二)

    十二.敌人受攻击时的闪烁和Player的生命值的修正 上一篇中,我们利用Controller2D中的IEnumerator TakenDamage接口,使得我们的Player受到攻击时会进行闪烁,我们 ...

  9. 【CSON原创】基于HTML5的横版射击游戏发布

    功能说明: 基于HTML5的横版射击游戏,参考自flash游戏<双面特工>.左右方向键控制移动,下方向键蹲下,上方向键跳跃,空格键射击.体验前请先关闭输入法. 该游戏基于自己开发的HTML ...

最新文章

  1. 谷歌最新论文:从图像中进行3-D目标检测
  2. 网络延迟和数据包丢失对网络性能的影响—Vecloud微云
  3. c# 学习笔记 (2) 窗体之间互相调用的方法
  4. TEMPO研究第一年影像学数据: 骨侵蚀修复几乎只出现在无关节肿胀或肿胀改善组...
  5. 微信红包随机数字_微信红包的随机算法
  6. Tensorflow入门----占位符、常量和Session
  7. 年薪 37 万+,挪威科技大学(NTNU)招募博士,利用 ML 和 AI 进行钢桥结构健康监测和损伤检测...
  8. 写好代码,拒绝一传到底的参数类
  9. 整洁数据 —tidytext(一)
  10. 这 4 款电脑记事本软件,得试试
  11. linux 中meltdown指令,用代码切身实践体会meltdown漏洞——初学者的体验感受
  12. 登陆apple pay显示无法登录服务器,apple pay无法添加卡怎么办?未能连接到apple pay的解决方法...
  13. 高级系统架构师培训笔记
  14. Eclipse/Myeclipse自定义JSP模板
  15. nginx证书绑定及二级域名反向代理
  16. 室内定位技术之UWB篇
  17. 实战:无线wifi密码破解与路由器入侵
  18. 扫地机器人半湿拖布_居然能自己洗拖布?云鲸智能扫拖一体机帮你真正解放双手...
  19. linux 查找某个程序,Linux查找特定程序whereis实例详解
  20. 怎样在Word文档中插入GIF动画

热门文章

  1. OpenCV人脸识别:识别是否是人脸,是的话用红线把人脸框出来
  2. Spring 学习,看松哥这一篇万余字干货就够了!
  3. python疲劳检测
  4. 在公司里,如何写好一封电子邮件
  5. Matlab/Simulink#直驱永磁风电机组并网仿真模型
  6. 宏景eHR SQL注入漏洞复现(CNVD-2023-08743)
  7. 使用VMware搭建3台一模一样的Linux虚拟机
  8. mysql5.7只安装服务端_mysql 5.7安装
  9. 敢不同才够硬核,从飞利浦欧风系列看电视产品的突围之路
  10. 如何压力测试电子商务网站