Unity 2-7 Stealth秘密行动

Abstract:向量运算;Animation动画;Navigation寻路系统;Mecanim动画系统

任务1&2&3:游戏介绍
&& 创建工程和游戏场景介绍 && 创建游戏环境

逃生游戏,过关条件为拿到钥匙并从电梯处逃脱
被敌人/ 摄像头/ 触碰红外线 -- 触发警报
红外线可以手动断电

右上图场景是美工创建好的
  Import package->Custom package: StealthAssets.unitypackage

资源介绍:
  Animation, Audio, Fonts, Gizmos, Materials, Models, Shaders, Textures

Animation中的.fdx文件是从3d软件中导出的动画模型
humanoid动画可以应用在任意人形模型上

Audio:声音文件
Fonts:字体
Gizmos:waypoint的图标,用于敌人寻路的AI,用图标表示路径
Materials:材质
Models:模型(环境模型、物品模型等)
  里面有一个文件夹Collision Meshes,存放collision的mesh,用于碰撞检测
  比如prop_cctvCam_collision: 一个椎体,用来检测player是否进入了摄像头视野
Shaders:更好的效果
Textures:模型的贴图

创建游戏环境:
  1. 新建文件夹Scenes,保存场景Stealth
  2. 创建空物体env,用于存放有关环境的物体
  3. 将Models->env_stealth_static拖入场景(env下)
    Reset Transform
  4. 给env_stealth_static添加碰撞器
    添加Mesh Collider
    在Mesh属性中指定简化的Models->Collision Meshes:
      env_stealth_collision->env_stealth_collision_001
    在scene模式下,可以看到的绿色网格就是刚才的collision

创建battleBus:
  发现中间一块有一个突起的mesh collider,而没有物体存在
  由于在env_stealth_static中没有放入位于地图中间的小车

因此手动添加prop_battleBus到地图内 (作为env子物体),并旋转到车头朝向杂物,位置与Mesh Collider重合即可

任务4:添加环境灯光

给环境添加环境灯光:
  在最外层创建空物体light (位置归零),用于存放所有灯光
  在light中创建Directional Light,颜色为暗一点的灰色,Intensity调小(0.1)
  Main Camera背景颜色调为黑色,Clear Flags调为Solid color

给房间添加灯光:
  在light中创建Point Light,颜色为暗橘黄色,
  一个房间添加一个,在四周的墙上或过于阴暗的地方放置若干

任务5&6:添加警报灯 && 添加警报声

实现警报灯
添加Directional Light: AlarmLight,红色,触发警报的时候Intensity在0.1~0.5之间不断变化
在AlarmLight上添加脚本AlarmController.cs

public bool isAlarmOn = false; // Flag
private float lowest/highestIntensity = 0.1/ 0.5; // 指定最高值最低值
private float targetIntensity; // 当前需要往lowest还是highest靠近

在Awake()中:
  targetIntensity = highestIntensity; // 默认是从lowest变到highest的
  isAlarmOn = false; // 确保刚开始警报未触发

在Update()中实现警报灯闪烁的效果:
  if(isAlarmOn) { // 警报开启状态
  light.intensity=Mathf.Lerp(ligh.intensity, targetIntensity, Time.deltaTime*speed);

现在的警报实现了从lowestIntensity到highestIntensity的变亮过程
  判断接近 (Lerp是一个无限趋近的过程) targetIntensity后,改变target的值

  if(Mathf.Abs(light.intensity - targetIntansity) < 0.05f) { // 达到目标值
    if(targetIntensity == highestIntensity) {
    targetIntensity = lowestIntensity;
  } else { 同理; }

当警报消除的时候,需要将Intensity设为0(直接设置会显得不自然,采用Lerp方法)
  light.intensity = Mathf

但是(个人认为)这么写需要一直执行intensity的赋值操作

为了让外界访问AlarmController.cs方便,采用单例模式
  public static AlarmController _instance;
  在Awake()中:_instance = this;
  在外界直接访问_instance即可

public class AlarmController : MonoBehaviour {
public static AlarmController _instance; public bool isAlarmOn = false;
private Light alarmLight;
private float lowestIntensity = 0.1f;
private float highestIntensity = 0.75f;
private float targetIntensity;
private float currentIntensity;
private float intensityChangeSpeed = 5f; void Awake () {
_instance = this;
targetIntensity = highestIntensity;
currentIntensity = lowestIntensity;
isAlarmOn = false;
alarmLight = GetComponent<Light>();
} void Update() {
if (isAlarmOn) {
if (Mathf.Abs(currentIntensity - targetIntensity) < 0.05) {
if (targetIntensity.Equals(highestIntensity)) {
targetIntensity = lowestIntensity;
} else {
targetIntensity = highestIntensity;
}
}
currentIntensity = Mathf.Lerp(currentIntensity, targetIntensity,
Time.deltaTime * intensityChangeSpeed);
} else {
// 警报处于关闭状态
currentIntensity = Mathf.Lerp(currentIntensity, ,
Time.deltaTime * intensityChangeSpeed);
}
alarmLight.intensity = currentIntensity;
}
}

实现警报声效果:

在env_stealth_static模型里会找到若干个喇叭: prop_magaphone
对这六个喇叭添加AudioSource组件,用于播放声音 alarm_triggered
取消勾选play on awake
将Min Distance设置大一点 (5)
之后需要播放警报声时,需要找到这六个喇叭,因此把他们设置为tag=Siren

任务7:游戏控制器GameController -- 控制整个游戏的运行

游戏控制器:
  灯光、声音、警报主角位置(比如主角位置暴露了,需要记录看到主角的最新位置,让机
  器人朝着最新位置移动)

创建空物体GameController,添加GameController.cs脚本

需要控制警报灯:
  public bool isAlarmOn = false;
  在Update()中将isAlarmOn传递给任务6中的单例模式AlarmController
    // 因为要随时控制isAlarmOn的值
    AlarmController._instance.isAlarmOn = this.isAlarmOn;

需要控制警报声:
  // 得到六个警报喇叭
  private GameObject[] sirens = GameObject.FindGameObjectsWithTag("Siren");
两个方法:分别控制警报声的响起和停止
  private void PlaySiren() {
    foreach (GameObject siren in sirens) {
      if(!siren.audio.isPlaying) { // 如果没有播放
        siren.audio.Play(); // 新版unity改用GetComponent AudioSource
  }}}
  private void StopSiren() { // 相似,但是不需判断当前是否正在播放 }
在Update()中,控制警报声的播放和停止
  if(isAlarmOn) {
    PlaySiren();
  } else {
    StopSiren();
  }

public class GameController : MonoBehaviour {
public bool isAlarmOn = false;
private GameObject[] sirens; private void Awake() {
isAlarmOn = false;
sirens = GameObject.FindGameObjectsWithTag("Siren");
} private void Update() {
AlarmController._instance.isAlarmOn = this.isAlarmOn;
if (isAlarmOn) {
PlaySiren();
} else {
StopSiren();
}} private void PlaySiren() {
foreach (GameObject siren in sirens) {
if (!siren.GetComponent<AudioSource>().isPlaying) {
siren.GetComponent<AudioSource>().Play();
}}} private void StopSiren() {
foreach (GameObject siren in sirens) {
siren.GetComponent<AudioSource>().Stop();
}}
}

任务8&9:实时摄像机CCTV Camera && 摄像机的自动旋转
任务10:摄像机的警报触发功能

在Prefab中可以找到 prop_cctvCam

在环境中需要有三个cctv Camera,分别在

创建空物体Camera,用于放置所有cctvCamera

第一个camera放在bus的两桶油下方
  摄像机的旋转(Joint)通过x和y的旋转实现,设置为低头60°
给摄像机添加灯光
  在cctv_cam_body上添加Light组件,Light设为Spot(探照灯)
  Light的cookie设置为texture: fx_cameraView_alp,颜色改为红色
将cctv_collision添加进cctv_cam_body,调整位置
  取消cctv_collision的renender渲染
  给collision添加mesh collider碰撞器,用于碰撞检测
将上面的cctv_Camera做成prefab

将其他两个camera分别放置

摄像机的旋转

通过Animation的方式,控制joint部分的y轴旋转

1. 新建Animator -- Window->Animation->Create CameraSweepAnimation.anim

2. Add Property: Transform->Rotation

3. 在对应时间点上添加KeyFrame,并设置Rotation.y的值(90~0~-90)
  Sample为每秒的帧数

4. 因为左下角的摄像机不需要进行旋转,因此不能apply to prefabs
  在另一个需要旋转的摄像机上添加Animator组件,并赋值,即可

摄像机的警报触发:

思路:在左下角摄像机中完成警报触发功能,并apply to prefabs

1. 在prop_cctvCam的collision子物体上添加脚本CctvCamCollision.cs

2. 将碰撞器设置为Trigger,因为不需要有物理碰撞效果

3. OnTriggerEnter(Collider other) {}
  if(other.tag.Equals("Player")) { // 触发警报 }

4. 触发警报需要设置GameController.cs中的isAlarmOn
  GameController设置为单例模式
  public static GameController _instance;
  _instance = this;

设置isAlarmOn:
  GameController._instance.isAlarmOn = true;

5. 需要记录当前警报触发位置
  在GameController.cs中
  public Vector3 lastPlayerPos;
  GameController._instance.lastPlayerPos = other.transform.position;

6. 使用OnTriggerStay()更好,因为当Player在其中移动的时候,会触发位置更新

任务11:标签的管理(代码管理)

使用代码进行标签的管理(使用字符串的过程中很可能出现字符串打错等情况)

创建脚本Tags.cs
  // 注意:Tags不是作为一个组件存在的,只是存放了一些变量

public const string player = "Player";
  // const -- 常量(tags不需要修改)

使用的时候:
  if(other.tag == Tags.player) {}

还有其他tags:
  "Siren"、"Enemy"等

任务12&13:添加激光警报装置 && 警报的触发和闪烁

fx_laserfence_laser: 
  调整大小、角度和位置(门柱上正好有孔,与laser一一对应)

1. 给laser添加Collider组件,用于碰撞检测 -- BoxCollider,选择Trigger

2. 给laser添加light组件,发光:PointLight、范围变小、红色、强度增大

3. 给laser添加Audio Source组件: Audio->laser_ham,发声:
Spetial Blend、min/max distance、PlayOnAwake、Loop

4. 做成Prefab

5. 创建空物体lasers,放入另外五个激光警报(一共6个)即可

激光警报的触发:

添加脚本LaserController.cs

OnTriggerStay() {
  // 和摄像头触发警报一样的操作
  // 因此在GameController中写成函数public void SwitchAlarmOn(Transform t)
  if(tag....) GameController._instance.SwitchAlarmOn(other.transform);

Apply to prefabs.

激光警报的闪烁:

两个激光(最长的那两条)需要间隔闪烁,方便Player的通过

在LaserController中:

public bool isFlicker;
public float onTime = 3f;
public float offTime = 1.5f;
private float timer = ; private void Update() {
if(isFlicker) {
if(this.gameObject.GetComponent<Renderer>().enabled) {
// 当前亮着
timer += Time.deltaTime;
if(timer >= onTime) {
this.gameObject.GetComponent<Renderer>().enabled = false;
timer = ;
}
} else {
// 当前暗着
timer += Time.deltaTime;
if(timer >= offTime) {
this.gameObject.GetComponent<Renderer>().enabled = true;
timer = ;
}}}}

(Collider也需要禁用和启用) --  
  gameObject.GetComponent<BoxCollider>().enabled;

在Inspector中将两个需要闪烁的激光的isFlicker属性设置为true
两个激光闪烁不同步,一个为(1.6, 2.8), 一个为(2.1, 2.5)(随意)

任务14&15:主角和主角的动画

添加主角:

Models->char_ethan

给Player添加碰撞器Capsule Collider

给Player添加刚体Rigidbody
  因为主角不需要通过rigidbody进行移动(使用动画控制),所以勾选IsKinematic
    (在任务16中会发现这是一个bug)
  主角在(x, z)平面上移动,且只围绕y轴旋转,所以Freeze Position: y; Rotation: x, z

主角移动的动画:

在Humanoid中的动画是人形动画,可以使用

新建一个Animator Controller,名为PlayerController

打开Animator编辑器(选中PlayerController,Window->Animator)

1. 主角的状态:
  walk/ run (walk和run是同一个动作,只不过速度不同 -- 相同动画)
  sneaker
  idle
  death

2. 添加parameter
  float: speed -- 移动速度
  bool: sneaker -- 是否处于sneaker状态(按下shift键)

3. 默认状态:idle
  将humanoid_idel->Idle动画拖入状态机(黄色为默认动画)

4. 右键Create State -> From Blend Tree
  (上面提到walk和run是同一个动作,用BlendTree实现)
  命名为Locomotion (移动、运动)
  双击进入编辑模式
  Add Motion Field,添加Walk和Run动画
  在Parameter中选择speed,用speed来控制walk和run的融合
  uncheck Automate Thresholds (自动生成参数)
    取消勾选后可以手动确定:参数是根据哪个值来确定的,这里选择speed
    
    这里表示1.555在Walk,speed增大渐渐进入run的状态,5.667为run状态

5. Idle->Locomotion
  make transition: Conditions: speed > 0.1
  Locomotion->Idle: speed < 0.1

6. Sneak: humanoid_sneak: sneak动画拖入状态机
  Idle->Sneak:
    make transition: Conditions: speed > 0.1 && sneak == true;
  Sneak->Idle: speed < 0.1

7. Locomotion->Sneak:
  speed > 0.1 && sneak == true;
  Sneak->Locomotion:
  speed > 0.1 && sneak == false;

由于player的移动是通过动画实现的
  因此如果觉得移动过慢/快,可以修改对应状态的speed

8. Dying: humanoid_dying->Dying
  添加parameter: bool dead
  因为任何状态下都会死亡 --
    AnyState->Dying: make transition: dead = true;

任务16&17:控制主角的运动和移动

运行游戏,手动调整主角的速度,会发现主角出现了移动,主角的transform也发生变化
  由于Player->Animator勾选了属性Apply Root Motion,表示动画会影响transform的值

思路:按下上下左右键时,设置speed即可
  根据按键控制朝向

在char_ethan添加脚本PlayerController.cs

在Update()中实现:

// 得到按键的信息
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");

// 需要得到Animator来改变speed的值
private Animator anim = GetComponent...

(我的思路)
  anim.SetFloat("Speed", (Mathf.Max(h,v) * 5.56f));
  -- 结果为不适合
    1. 数值变化太快,walk->run过于迅速
    2. 而且延迟较大,Input.GetAxis()的值需要时间来归零和增大

(正确思路 -- 利用差值)
  if(Mathf.Max(Mathf.Abs(h), Mathf.Abs(v)) > 0.1f) {
    // 开始移动
    newSpeed = Mathf.Lerp(anim.GetFloat("speed"), maxSpeed, delta...)
  } else { // 停止移动
    // 相同思路,利用差值,但是将speedRate增大,因为需要很快停止移动
    // 试验过后发现还是不好控制,于是直接将speed设为0
    // 但是从跑步到Idle的动画很不平滑 -- 自行选择
    newSpeed = 0;
  }
  anim.SetFloat("speed", newSpeed);

出现bug -- Player会进行穿墙,就好像Collider没有任何作用一样
  原因:任务14中给Player添加Rigidbody时勾选了IsKinemetic选项,取消勾选即可

控制Player的移动方向:
  在需要移动的if条件中:
    // 取得目标方向
    Vector3 targetDir = new Vector3(h, 0, v);
    // 取得当前方向
    Vector3 currDir = transform.forward;
    // 取得两个方向的夹角
    float angle = Vector3.Angle(targetDir, currDir);
    // 使用匀速旋转的方法
    transform.Rotate(Vector3.up * angle * Time.deltaTime * rotateSpeed);
    // rotateSpeed可设为5

到此为止player的移动功能实现了,但是
  出现Bug --

1. 角色在停止控制后有的时候会继续不听旋转
  (个人推测是惯性?)
  将角色的Rotation: y Freeze即可

2. 拐弯的时候会出现有的时候往大弯绕
  比如:角色朝左,向下控制,角色会通过上右下而向下移动
     angle的值也是从90变大到180再变小
     细心观察会发现,角色永远是顺时针旋转
  原因:Vector3.Angle()返回值没有正负,小于180°
     Rotate(...)方向为Vector3.up,因此永远是顺时针旋转
       而旋转一些以后,angle值变大,需要继续旋转,直到angle值为0
  解决方法 -- 想不出如何判断使用Vector3.up还是Vector3.down
    舍弃该方法
    使用Quaternion和Slerp来解决
    Quaternion targetQuaternion = Quaternion.LookRotation(targerDir);
    // 四元数返回的是一个带有方向的角度 (该函数默认以Vector3.up为上方)
    transform.rotation = Quaternion.Slerp(transform.rotation,
      targetQuaternion, Time.deltaTime * rotationSpeedRate);

private Animator animator;
private float increaseSpeedRate = 2.5f;
private float decreaseSpeedRate = 60f;
private float rotateSpeedRate = ;
private float newSpeed;
private const float maxSpeed = 5.66f; private void Awake() {
animator = GetComponent<Animator>();
} void Update() {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
if (Mathf.Max(Mathf.Abs(h), Mathf.Abs(v)) > 0.1f) {
// 开始移动
newSpeed = Mathf.Lerp(animator.GetFloat("speed"), maxSpeed,
Time.deltaTime * increaseSpeedRate);
// 开始旋转
// 取得目标方向
Vector3 targetDir = new Vector3(h, , v);
Quaternion targetDirQuaternion = Quaternion.LookRotation(targetDir);
transform.rotation = Quaternion.Slerp(transform.rotation,
targetDirQuaternion, Time.deltaTime * rotateSpeedRate);
} else {
// 停止移动
newSpeed = Mathf.Lerp(animator.GetFloat("speed"), ,
Time.deltaTime * decreaseSpeedRate);
// newSpeed = 0;
}
animator.SetFloat("speed", newSpeed);
}

实现sneak缓慢行走:

if(Input.GetKeyDown(KeyCode.LeftShift)) {
  anim.SetBool("sneak", true);
}
if(Input.GetKeyUp(KeyCode.LeftShift)) {
  anim.SetBool("sneak", false);
}

会发现,从Idle时按住shift后再按方向键,刚开始一小小段时间会进行walk而不是sneak
解决方法 --
  将动画状态机中Idle->Locomotion的condition加上sneaker=false;即可

此时sneak的速度有点慢
  控制sneak的动画播放速度即可
  选中动画状态机中的sneak,将inspector中的speed加大即可

任务18:主角行走声音和游戏背景音乐

添加背景音乐:

在GameController中添加AudioSource: music_normal,PlayOnAwake,Loop,2D

添加行走声音:

在主角上添加AudioSource: player_footsteps

在PlayerController.cs中添加两个方法

void PlayFootstepsAudio() {
  if(!audio.isPlaying) {
    audio.Play();
}}

void StopFootstepsAudio() {
  audio.Stop();
}

在Locomotion的状态下需要播放声音,其他状态不要

if(anim.GetCurrentAnimatorStateInfo(0).IsName("Locomotion")) {
  // play audio
} else {
  // stop audio
}

切换背景音乐:

当警报被触发的时候,背景音乐会从music_normal切换至music_panic
思路:如果直接暂停播放会显得比较突兀,因此使用对两个Volume进行差值运算的方法

在GameController上添加另一个AudioSource: music_panic, Loop

在GameController.cs中:
  定义两个AudioSource来存放两个声音
  在Update()中修改两个AudioSource的Volume
    在原有的isAlarmOn判断下
    musicNormal.volume=Mathf.Lerp(musicNormal.volume, 0, Time.deltaTime);
    musicPanic.volume=Mathf.Lerp(musicPanic.volume, 1, Time.deltaTime);
    !isAlarmOn时也类似
  也可以添加一个musicFadeSpeed来控制渐变速度
  Panic的背景声音有点大,因此改为0.5f

任务19:添加自动门

自动门机制:当主角靠近门的时候会门会自动打开: door_generic_slide
  门的模型都自带两个动画 open和close

创建空物体,用于存放所有门

使用Animator状态机进行门的控制
  新建Animator,名为normalDoorAnimationController
  将两个动画拖入状态机 -- 默认为close状态,设为default
  添加Parameters: bool closing; // 当closing为true时,进行关闭,为false时进行开启
    closing初始值为true
  添加状态close和open之间的transition

给door_generic_slide->door_generic_slide_panel 添加BoxCollider来检测碰撞
给door_generic_slide添加sphere collider,用于trigger开门动画

在door上添加DoorController.cs脚本 -- 代码控制门的开关
  因为是控制门的开关,因此如果是Enemy或Player在触发区内,保持开门状态
  OnTriggerStay(Collider other) {
    if(other.tag == Tags.Player || ... == Tags.Enemy) {
      anim.SetBool("closing", false);
  }}
  // 那如何关门呢?OnTriggerExit()? 不行,如果区域内有两个人的话,怎么解决?
  // 使用count来计数 -- 就不能用OnTriggerStay了
  OnTriggerEnter(...) {
    if(other.tag ... || ... ) {
      count++;
  }}
  OnTriggerExit(...) { // 相同情况 count--; }

  在Update中用count来判断是否开关门
            doorAnimator.SetBool("closing", (count<=0));

添加开关门的声音:打开和关闭时播放声音
  在门上添加AudioSource: door_open
  在Update中更改doorAnimator.closing时
    // 播放声音
    if(anim.IsInTransition(0) {
      // 如果在0 layer中正在进行某个Transition
      audioDoorOpen.Play();

将门做成Prefab,并创建其他两扇门

任务20:添加电梯门 -- 设置内侧外侧打开和关闭动画

电梯的自动门是两扇,往两边打开
  电梯门分为外侧和内侧两扇门(内侧门即电梯,跟随上下移动的)

添加外侧门:door_exit_outer

相同的,分别给door_exit_outer_left和right添加上BoxCollider
  给door_exit_outer添加上SphereCollider作为Trigger
  AudioSource为door_open

外侧门的控制代码使用上一任务的DoorController.cs即可
  Animator使用上一任务的NormalDoorController即可
    -- 复制一个AnimatorController,并将状态中的动画(Motion)换成对应的动画
       door_exit_outer_close和door_exit_outer_open

添加内侧门:prop_lift_exit

注意电梯门的方向 prop_lift_exit->door_exit_inner

在Lift上添加脚本LiftController.cs
  // 让inner door的z轴坐标跟随outer door的x轴坐标变化即可
  // 首先得到是个transform: inner_door和outer_door的
  innerDoorLeft.position = new Vector3(outerDoorLeft.position.x, 
    innerDoorLeft.position.y, innerDoorLeft.position.z);
        innerDoorRight.position = new Vector3(outerDoorRight.position.x, 
    innerDoorRight.position.y, innerDoorRight.position.z);

内侧门的开关实现了,但是速度会比较快,因此采用另一种方法 -- Lerp
  newInnerDoorLeftX = Mathf.Lerp(innerDoorLeft.position.x, 
    outerDoorLeft.position.x, Time.deltaTime);

再给内侧门左右加上BoxCollider

任务21:对电梯门添加钥匙控制

因为两种门共用一个脚本DoorController.cs
  使用一个bool keyRequired = false; 来判断该门是否需要钥匙

Siki_Unity_2-7_Stealth秘密行动的更多相关文章

  1. TypeScript: Angular 2 的秘密武器(译)

    本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch?v=e3djIqAGqZo 开场白 开场白主要分为三部分: 感谢了 ...

  2. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  3. 网站的SEO以及它和站长工具的之间秘密

    博客迁移没有注意 URL 地址的变化,导致百度和 google 这两只爬虫引擎短时间内找不到路.近段时间研究了下国内最大搜索引擎百度和国际最大搜索引擎google的站长工具,说下感受. 百度的站长工具 ...

  4. 《WePayUI组件设计的秘密》——2016年第一届前端体验大会分享

    本文是博主参加第一届前端体验大会 | 物勒工名做的分享<WePayUI组件设计的秘密>,内容主要分为2个部分: 一.浅析UI库/框架的未来 讨论的UI库或者框架,主要包含展示和交互的css ...

  5. [从产品角度学EXCEL 03]-单元格的秘密

    这是<从产品角度学EXCEL>系列——单元格的秘密. 前言请看: 0 为什么要关注EXCEL的本质 1 EXCEL是怎样运作的 2 EXCEL里的树形结构 或者你可以去微信公众号@尾巴说数 ...

  6. 2016第16本:TED演讲的秘密

    花0.01元抢购了<得到APP>中的<成甲说书:TED演讲的秘密>,不到30分钟的音频,感觉全是干货,基本不用看原书了.如果在以后的演讲中随便应用几条都可以让演讲水平提升一大截 ...

  7. 字符串的replace()方法隐藏着什么不可告人秘密?

    最近在做JS算法项目时发现一个令我匪夷所思的问题, 这里想记录一下问题. 首先介绍一下字符串replace()方法的基本用法. replace() 方法使用一个替换值(replacement)替换掉一 ...

  8. 第一章-第七题( 有人认为,“中文编程”, 是解决中国程序员编程效率一个秘密武器,请问它是一个 “银弹” 么? )--By 侯伟婷

    首先,“银弹”在百度百科中的解释是银色的子弹,我们更熟知的“银弹”一词,应该是在<人月神话>中提到的.银弹原本应该是指某种策略.技术或者技巧可以极大地提高程序员的生产力[1].此题目中关于 ...

  9. CPU阿甘:函数调用的秘密

    个人感言:真正的知识是深入浅出的,码农翻身" 公共号将苦涩难懂的计算机知识,用形象有趣的生活中实例呈现给我们,让我们更好地理解.感谢"码农翻身" 公共号,感谢你们的成果, ...

  10. [转]了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密_Mr_Indigo的空间

    了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密 关系型数据库,如SQL Server,使用锁来避免多用户修改数据时的并发冲突.当一组数据被某个用户锁定时,除非第一个用户结束修 ...

随机推荐

  1. C语言的谜题

    本篇文章<C语言的谜题>展示了14个C语言的迷题以及答案,代码应该是足够清楚的,而且我也相信有相当的一些例子可能是我们日常工作可能会见得到的.通过这些迷题,希望你能更了解C语言.如果你不看 ...

  2. Reading SBAR SDN flow-Based monitoring and Application Recognition

    概要 在sdn下,控制平面基于网络测量的的数据控制网络,而细粒度的管理得益于细粒度的测量数据.针对sdn环境下的细粒度测量(识别具体应用程序),可以实现对细粒度的流量管控. 设计了识别系统SBAR,对 ...

  3. SimpleDateFormat 的 format 方法使用具体解释

    Java中怎么才干把日期转换成想要的格式呢.或把字符串转换成一定格式的日期,如把数据库中的日期或时间转换成自己想要的格式,JAVA中提供了SimpleDateFormat类能够实现,下面是Simple ...

  4. JDBC——连接数据库的代码

    第一步:在SCR下创建一个file,写好数据库的相关信息. #oracle数据库 driver=oracle.jdbc.driver.OracleDriver jdbcUrl=jdbc:oracle: ...

  5. ABAP术语-Update Module

    Update Module 原文:http://www.cnblogs.com/qiangsheng/archive/2008/03/20/1114178.html Part of an update ...

  6. JQuery弹出Dialog关闭方式close vs destroy

    $editDialog.iDialog('close')  $(this).dialog('close'); 等Close方法关闭Dialog时,Dialog并不是完全消失,只是隐藏起来.两个Dial ...

  7. XML第一次简单入门(Lab分析)

    In this tutorial you will create a well-formed and verified XML file. Consider the XML document belo ...

  8. 【vue】------浅谈vue------【William】

    ### Vue > Vue是一个前端js框架,由尤雨溪开发,是个人项目 Vue近几年来特别的受关注,三年前的时候angularJS霸占前端JS框架市场很长时间,接着react框架横空出世,因为它 ...

  9. thinkphp3.2+cropper上传多张图片剪切图片

    实现效果截图 点加号可以继续上传第二张图片 代码部<--引入cropper相关文件--> <link rel="stylesheet" href="/h ...

  10. 详解 Python3 正则表达式(三)

    上一篇:详解 Python3 正则表达式(二) 本文翻译自:https://docs.python.org/3.4/howto/regex.html 博主对此做了一些批注和修改 ^_^ 模块级别的函数 ...