hav-cs50-merge-07

哈佛 CS50 中文官方笔记(八)

第六讲

原文:cs50.harvard.edu/r/notes/6/

  • 欢迎!

  • 异常

  • 信息

  • 警告warning

  • 停止stop

  • 单元测试

  • testthat

  • 浮点数测试(testing-floating-point-values

  • 容忍度tolerance

  • 测试驱动开发

  • 行为驱动开发

  • 测试覆盖率

  • 总结

欢迎!

  • 欢迎回到 CS50 的 R 语言编程入门课程!

  • 今天,我们将学习关于程序测试的内容。我们将了解程序可能会出错的地方,当它们出错时我们如何处理,以及如何系统地测试我们的程序以确保它们按预期运行!

异常

  • 考虑以下计算平均值的程序:

    # Define function to calculate average value in a vector  average  <-  function(x)  {  sum(x)  /  length(x)  } 
    

    注意这个程序尝试将其输入作为一个数字向量,并输出平均值。

  • 你可以想象用户可能会意外地传递字符而不是数字,导致我们的average函数输出错误。

  • 这些错误被称为异常。有没有可能检查潜在的这种异常呢?考虑以下对average的更新:

    # Handle non-numeric input  average  <-  function(x)  {  if  (!is.numeric(x))  {  return(NA)  }  sum(x)  /  length(x)  } 
    

    注意一个条件语句,一个if语句,是如何检查向量x是否不全是数字。在 R 世界的惯例中,在这种情况下返回一个值NA是合适的。

信息message

  • 虽然这允许我们的程序无声运行,但我们可能希望让用户知道发生了异常。一种通知用户的方式是通过message函数:

    # Message about returning NA  average  <-  function(x)  {  if  (!is.numeric(x))  {  message("`x` must be a numeric vector. Returning NA instead.")  return(NA)  }  sum(x)  /  length(x)  } 
    

    注意程序返回NA的原因是通过message发送给用户的。

  • 传统上,message是在没有出错时使用的:message纯粹是信息性的。因此,我们可以通过warning提升这条信息的重要性。

警告warning

  • 我们可以将我们的message的重要性提升到warning,如下所示:

    # Warn about returning NA  average  <-  function(x)  {  if  (!is.numeric(x))  {  warning("`x` must be a numeric vector. Returning NA instead.")  return(NA)  }  sum(x)  /  length(x)  } 
    

    注意现在输出的是一个警告信息。

  • 一个warning不会完全停止程序,但它确实让程序员知道出了问题。

停止stop

  • 你可以想象一些情况,你并不只是想警告用户;你可能想完全停止函数。考虑以下:

    # Stop instead of warn  average  <-  function(x)  {  if  (!is.numeric(x))  {  stop("`x` must be a numeric vector.")  }  sum(x)  /  length(x)  } 
    

    注意stop告诉用户,由于他们提供的输入,我们无法继续。

  • 也可以将两种可能性结合起来。例如,以下代码检查了x包含非数字元素的情况。同样,此代码也适应了存在NA值的情况:

    # Handle NA values  average  <-  function(x)  {  if  (!is.numeric(x))  {  stop("`x` must be a numeric vector.")  }  if  (any(is.na(x)))  {  warning("`x` contains one or more NA values.")  return(NA)  }  sum(x)  /  length(x)  } 
    

    注意提供了两个if语句。

单元测试

  • 单元测试用于测试我们的函数和程序。

  • 考虑以下在单独文件中对average进行的测试函数:

    # Write test function  source("average6.R")  test_average  <-  function()  {  if  (average(c(1,  2,  3))  ==  2)  {  cat("`average` passed test :)\n")  }  else  {  cat("`average` failed test :(\n")  }  }  test_average() 
    

    注意到这个函数提供了一个测试案例,其中将数字 123 传递给 average 函数。然后,提供了一些反馈。注意在第一行,source 确保这个测试文件可以访问 average 函数。

  • 测试负数也是明智之举:

    # Add test cases  source("average6.R")  test_average  <-  function()  {  if  (average(c(1,  2,  3))  ==  2)  {  cat("`average` passed test :)\n")  }  else  {  cat("`average` failed test :(\n")  }  if  (average(c(-1,  -2,  -3))  ==  -2)  {  cat("`average` passed test :)\n")  }  else  {  cat("`average` failed test :(\n")  }  if  (average(c(-1,  0,  1))  ==  0)  {  cat("`average` passed test :)\n")  }  else  {  cat("`average` failed test :(\n")  }  }  test_average() 
    

    注意到为正数、负数和零提供了额外的测试。

  • 我们已经编写了 21 行代码!幸运的是,程序员已经创建了各种测试包或库,可以用来测试我们的代码。

testthat

  • testthat 是一个用于测试 R 代码的包。可以通过在控制台中输入 library(testthat) 来加载。

  • testthat 包含一个名为 test_that 的函数,可以用来测试我们的函数:

    # Test warning about NA values  source("average6.R")  test_that("`average` calculates mean",  {  expect_equal(average(c(1,  2,  3)),  2)  expect_equal(average(c(-1,  -2,  -3)),  -2)  expect_equal(average(c(-1,  0,  1)),  0)  expect_equal(average(c(-2,  -1,  1,  2)),  0)  })  test_that("`average` warns about NAs in input",  {  expect_warning(average(c(1,  NA,  3)))  expect_warning(average(c(NA,  NA,  NA)))  }) 
    

    注意到 test_that 函数可以指示期望各种数字的平均值等于某个特定值,这要归功于 expect_equal。同样,我们也可以向 test_that 函数提供指令以 expect_warning,当平均计算包含 NA 值时。此外,注意测试被分为不同的部分。一个部分测试平均值的计算,而另一个部分测试警告。

  • 运行上述测试,我们发现我们的average函数中if语句的顺序可能是不正确的:

    # Fix ordering of error handling  average  <-  function(x)  {  if  (any(is.na(x)))  {  warning("`x` contains one or more NA values.")  return(NA)  }  if  (!is.numeric(x))  {  stop("`x` must be a numeric vector.")  }  sum(x)  /  length(x)  } 
    

    注意到条件语句的顺序被改变了。

  • 我们应该测试average在输入为NA值时返回NA,而不仅仅是average会引发警告!

    # Test NA return values  source("average7.R")  test_that("`average` calculates mean",  {  expect_equal(average(c(1,  2,  3)),  2)  expect_equal(average(c(-1,  -2,  -3)),  -2)  expect_equal(average(c(-1,  0,  1)),  0)  expect_equal(average(c(-2,  -1,  1,  2)),  0)  })  test_that("`average` returns NA with NAs in input",  {  expect_equal(suppressWarnings(average(c(1,  NA,  3))),  NA)  expect_equal(suppressWarnings(average(c(NA,  NA,  NA))),  NA)  })  test_that("`average` warns about NAs in input",  {  expect_warning(average(c(1,  NA,  3)))  expect_warning(average(c(NA,  NA,  NA)))  }) 
    

    注意到我们有两个独立的测试,将 NA 值作为输入传递给 average。一个测试正确的返回值,而另一个测试会引发一个 warning

  • test_that 有其他函数可以帮助我们进行测试,包括 expect_errorexpect_no_error

  • 使用 expect_error 我们可以修改我们的代码如下:

    # Test stop if argument is non-numeric  source("average7.R")  test_that("`average` calculates mean",  {  expect_equal(average(c(1,  2,  3)),  2)  expect_equal(average(c(-1,  -2,  -3)),  -2)  expect_equal(average(c(-1,  0,  1)),  0)  expect_equal(average(c(-2,  -1,  1,  2)),  0)  })  test_that("`average` returns NA with NAs in input",  {  expect_equal(suppressWarnings(average(c(1,  NA,  3))),  NA)  expect_equal(suppressWarnings(average(c(NA,  NA,  NA))),  NA)  })  test_that("`average` warns about NAs in input",  {  expect_warning(average(c(1,  NA,  3)))  expect_warning(average(c(NA,  NA,  NA)))  })  test_that("`average` stops if `x` is non-numeric",  {  expect_error(average(c("quack!")))  expect_error(average(c("1",  "2",  "3")))  }) 
    

    注意到当输入是“quack!”或提供字符而不是数字时,代码期望出现错误。

测试浮点值

  • 我们可能希望将浮点值(即十进制值)作为输入传递给 average

     # Test doubles  source("average7.R")  test_that("`average` calculates mean",  {  expect_equal(average(c(1,  2,  3)),  2)  expect_equal(average(c(-1,  -2,  -3)),  -2)  expect_equal(average(c(-1,  0,  1)),  0)  expect_equal(average(c(-2,  -1,  1,  2)),  0)  expect_equal(average(c(0.1,  0.5)),  0.3)  })  test_that("`average` returns NA with NAs in input",  {  expect_equal(suppressWarnings(average(c(1,  NA,  3))),  NA)  expect_equal(suppressWarnings(average(c(NA,  NA,  NA))),  NA)  })  test_that("`average` warns about NAs in input",  {  expect_warning(average(c(1,  NA,  3)))  expect_warning(average(c(NA,  NA,  NA)))  })  test_that("`average` stops if `x` is non-numeric",  {  expect_error(average(c("quack!")))  expect_error(average(c("1",  "2",  "3")))  }) 
    

    注意到在第一组测试的末尾添加了一个浮点值的测试。

容忍度

  • 浮点值是独特的,因为它们受到浮点数不精确性的影响。

  • 让我们通过例子来理解浮点数的不精确性:

    # Demonstrates floating-point imprecision  print(0.3)  print(0.3,  digits  =  17) 
    

    注意到在 R 中,0.3 并不是精确地表示为 0.3。这是编程语言中常见的现象,因为存在无限多的浮点值和有限的位数来表示它们。

  • 由于浮点数的不精确性,涉及浮点值的等式测试需要允许一定的容忍度。容忍度指的是一个范围,即高于或低于预期值,将被视为与预期值相等。容忍度通常以绝对值指定,例如 ± .000001。

  • expect_equal 函数已经提供了一种通常适用于大多数用例的容差级别。这个默认值可以通过 tolerance 参数进行更改。

  • 你和你的团队应该决定在计算中期望的精度水平。

测试驱动开发

  • 一种开发哲学被称为 测试驱动开发。在这种思维模式下,人们认为在编写将被测试的源代码之前先创建一个测试是最好的。考虑以下测试:

    # Test greet  source("greet1.R")  test_that("`greet` says hello to a user",  {  expect_equal(greet("Carter"),  "hello, Carter")  }) 
    

    注意你可以想象一个 greet 函数应该能够问候作为输入的用户。

  • 观察这个测试,我们可以编写响应测试的代码:

 # Greets a user  greet  <-  function(to)  {  return(paste("hello,",  to))  } 

注意这段代码是如何通过用户名向用户打招呼的。

  • 在测试驱动开发中,编写测试让程序员知道他们应该实现哪些功能。好处是这些功能随后可以立即进行测试。进一步的修改应该始终通过已经编写的测试。

行为驱动开发

  • 行为驱动开发 在精神上与测试驱动开发相似,但更侧重于函数在上下文中的行为。在行为驱动开发中,人们可能会通过明确命名函数应该做什么来描述我们希望函数执行的操作。

  • testthat 包含两个函数来实现行为驱动开发,describeit

    # Describe greet  source("greet2.R")  describe("greet()",  {  it("can say hello to a user",  {  name  <-  "Carter"  expect_equal(greet(name),  "hello, Carter")  })  it("can say hello to the world",  {  expect_equal(greet(),  "hello, world")  })  }) 
    

    注意 describe 包含了几个基于代码的描述,说明了 it(该函数!)应该能够做什么。

测试覆盖率

  • 当你开始为你的代码编写测试时,考虑这些测试的全面性。定义出你的代码需要完成的关键任务,并创建体现这些关键任务的测试。

总结

在本课中,你学习了如何在 R 中测试程序。具体来说,你学习了以下内容:

  • 异常

  • message

  • warning

  • stop

  • 单元测试

  • testthat

  • 测试浮点值

  • 容差

  • 测试驱动开发

  • 行为驱动开发

  • 测试覆盖率

次次见,届时我们将能够打包我们的代码并与世界分享。

第七讲

原文:cs50.harvard.edu/r/notes/7/

  • 欢迎!

  • 包结构

  • devtools

  • 编写测试

  • 编写 R 代码

  • NAMESPACE

  • 测试代码

  • 编写文档

  • 构建包

  • 更新包

  • 使用和共享包

  • 总结

欢迎光临!

  • 欢迎回到 CS50 的 R 编程入门课程!

  • 今天,我们将学习如何打包和分发我们的程序。这样,我们就可以与世界分享它们了!

  • 今天,我们将构建和打包一个名为 ducksay 的程序。

  • 如果你已经参加过我们的其他 CS50 课程,你可能知道一个名为 cowsay 的程序。它接受一些文本并创建一个牛说这些文本的图像。我们将以同样的精神构建 ducksay

  • 是经过编译的源代码,以便可以分发。

包结构

  • 包应该保存在一个与包同名的文件夹中。

  • 我们可以在 R 控制台中输入以下命令来做到这一点:

    dir.create("ducksay")  setwd("ducksay") 
    

    注意这些命令创建了一个名为 ducksay 的目录,然后将工作目录设置为 ducksay

  • 包通常在主文件夹中有以下结构:

    DESCRIPTION
    NAMESPACE
    man/
    R/
    tests/ 
    

    DESCRIPTION 文件将包括对包的描述,包括谁编写了它。NAMESPACE 文件将包括我们希望向我们的包用户提供的函数列表。man 是一个包含包手册(文档)的文件夹。R 包含包的 R 代码。最后,tests 包含我们想要运行的所有测试,以确保我们的包按预期行为。

  • 我们可以在 R 控制台中输入 file.create("DESCRIPTION") 来创建一个 DESCRIPTION 文件。现在我们可以打开这个文件并按照以下方式编码:

    # Demonstrates required components of a DESCRIPTION file  Package:  ducksay  Title:  Duck  Say  Description:  Say  hello  with  a  duck.  Version:  1.0  Authors@R:  person("Carter",  "Zenke",  email  =  "carter@cs50.harvard.edu",  role  =  c("aut",  "cre",  "cph"))  License:  MIT  +  file  LICENSE 
    

    注意包的命名和标题。然后,提供描述。包括作者。最后,提供提供此包的许可证。你可以在 DESCRIPTION 文件的 文档 中了解更多关于这些字段的信息。

  • 如上 DESCRIPTION 文件所示,我们还需要一个 LICENSE 文件。我们可以按照以下方式编码:

    # Demonstrates adding on to a license template  YEAR:  ...  COPYRIGHT  HOLDER:  ducksay  authors 
    

    用当前年份填充 ...。注意许可证和版权所有者的年份是如何命名的。

devtools

  • 一个名为 devtools 的包允许我们更快地创建包。

  • 特别是,devtools 包提供了创建我们包测试和 R 代码所需文件夹结构的工具。

  • 我们可以通过在 R 控制台中输入 library(devtools) 来加载 devtools,假设它已经安装。

编写测试

  • 感谢 devtools 包,我们可以轻松地使用 testthat 为我们编写的包开发测试。

  • 然后,我们可以输入 use_testthat() 来调用使用 testthat 的能力。我们的 DESCRIPTION 文件将自动修改如下:

    # Demonstrates suggesting a dependency, for testing's sake  Package:  ducksay  Title:  Duck  Say  Description:  Say  hello  with  a  duck.  Version:  1.0  Authors@R:  person("Carter",  "Zenke",  email  =  "carter@cs50.harvard.edu",  role  =  c("aut",  "cre",  "cph"))  License:  MIT  +  file  LICENSE  Suggests:  testthat  (>=  3.0.0)  Config/testthat/edition:  3 
    

    注意包会建议应该安装 testthat 版本 3.0.0 或更高版本。这可能会根据您安装的 testthat 版本而有所不同。

  • 在由 use_testthat 创建的 tests/testthat 文件夹内,我们可以创建我们的第一个测试,test-ducksay.R,如下所示:

    # Demonstrates describing behavior of `ducksay`  describe("ducksay()",  {  it("can print to the console with `cat`",  {  expect_output(cat(ducksay()))  })  it("can say hello to the world",  {  expect_match(ducksay(),  "hello, world")  })  }) 
    

    注意 expect_matchducksay 的输出中寻找字符串 hello, world

编写 R 代码

  • 继续使用 devtools,现在我们可以在 R 控制台中输入以下内容:use_r("ducksay")

  • 此命令将创建一个名为 R 的文件夹和一个名为 ducksay.R 的文件。

  • 现在,是时候在我们的程序中提供一些我们将打包的功能了。这个功能应该与我们所编写的测试相匹配。

  • 按照以下方式编写 ducksay.R 的代码:

    # Demonstrates defining a function for a package  ducksay  <-  function()  {  paste(  "hello, world",  ">(. )__",  " (____/",  sep  =  "\n"  )  } 
    

NAMESPACE

  • 如前所述,我们需要在名为 NAMESPACE 的文件中提供有关此包最终用户可用的函数的信息。

  • 要这样做,您可以在控制台中输入 file.create("NAMESPACE")。然后,按照以下方式编辑此文件:

    # Demonstrates declaring `ducksay` accessible to package end users  export(ducksay) 
    

    此文件仅使 ducksay 函数对包的最终用户可用。

  • 现在,我们可以在 R 控制台中输入 load_all() 来加载 NAMESPACE 中命名的所有可用函数。

测试代码

  • 现在,您可以通过运行 test() 来测试我们的函数。不应该出现任何错误。

  • 此外,在您加载了这个包的函数之后,现在您可以在 RStudio 中使用 ducksay

  • 更新我们的测试,让我们测试一下 ducksay 中是否出现了鸭子:

    # Demonstrates checking for duck in output  describe("ducksay()",  {  it("can print to the console with `cat`",  {  expect_output(cat(ducksay()))  })  it("can say hello to the world",  {  expect_match(ducksay(),  "hello, world")  })  it("can say hello with a duck",  {  duck  <-  paste(  ">(. )__",  " (____/",  sep  =  "\n"  )  expect_match(ducksay(),  duck,  fixed  =  TRUE)  })  }) 
    

    注意这个测试看起来是否表示了鸭子。此外,注意 fixed = TRUE,正如讲座中所述,它防止测试错误地解释鸭子中的一些字符作为称为正则表达式的东西的一部分。现在就足够了,正则表达式不是我们想要的!

编写文档

  • 现在,我们可以记录如何使用我们的函数。通常,我们可以输入 ?ducksay 来查看文档。然而,我们还没有创建我们的文档。

  • 文档是用一种称为标记语言的类型语言编写的。标记语言提供用于指定文档格式的语法。

  • 您可以通过以下方式编写文档:

    dir.create("man")  file.create("man/ducksay.Rd") 
    

    第一个命令创建一个名为 man 的文件夹。第二个创建我们的文档文件。

  • 按照以下方式修改您的文档文件:

    # Demonstrates required markup for R documentation files  \name{ducksay}  \alias{ducksay}  \title{Duck  Say}  \description{A  duck  that  says  hello.}  \usage{  ducksay()  }  \value{  A  string  representation  of  a  duck  saying  hello  to  the  world.  }  \examples{  cat(ducksay())  } 
    

    注意 nametitledescriptionusage 以及其他部分是如何提供的。您可以通过阅读有关 R 文档文件的文档来了解更多关于这些元素的信息。

  • 现在,是时候总结一下并分享我们的包了。

构建包

  • 一旦一个包的内容准备好打包和分发,可以使用两个命令中的任何一个来启动 构建

    build
    R CMD build 
    

    注意到 build 是一个 devtools 函数,可以直接在 R 控制台中运行。R CMD build 可以在 R 的计算机终端外运行。

  • 运行 build,你将在工作目录中看到一个 .gz 文件被输出。

更新包

  • 要更新我们的代码,我们可以打开我们的测试文件并更新测试如下:

    # Demonstrates ensuring duck repeats given phrase  describe("ducksay()",  {  it("can print to the console with `cat`",  {  expect_output(cat(ducksay()))  })  it("can say hello to the world",  {  expect_match(ducksay(),  "hello, world")  })  it("can say hello with a duck",  {  duck  <-  paste(  ">(. )__",  " (____/",  sep  =  "\n"  )  expect_match(ducksay(),  duck,  fixed  =  TRUE)  })  it("can say any given phrase",  {  expect_match(ducksay("quack!"),  "quack!")  })  }) 
    

    注意到添加了一个新的测试,用于查找“quack!”

  • 考虑到这个测试,我们现在可以更新我们的源代码,以便输入任何短语,然后鸭子会相应地表达出来:

    # Demonstrates taking an argument to print  ducksay  <-  function(phrase  =  "hello, world")  {  paste(  phrase,  ">(. )__",  " (____/",  sep  =  "\n"  )  } 
    

    注意到提供了一个默认的 phrase,“hello, world”。如果提供了另一个 phrase,它将说出那个 phrase

  • 同样,我们可以更新我们的文档文件如下:

    # Demonstrates updated markup, including specifying arguments  \name{ducksay}  \alias{ducksay}  \title{Duck  Say}  \description{A  duck  that  says  hello.}  \usage{  ducksay(phrase  =  "hello, world")  }  \arguments{  \item{phrase}{The  phrase  for  the  duck  to  say.}  }  \value{  A  string  representation  of  a  duck  saying  the  given  phrase.  }  \examples{  cat(ducksay())  cat(ducksay("quack!"))  } 
    

    注意到 value 已更新。此外,arguments 也已更新。另一个例子在 examples 中提供。

  • 我们可以再次运行 build 来包含我们的修改。

  • 现在我们可以将这个包与其他人共享。

使用和共享包

  • 现在让我们创建一个名为 greet.R 的程序,该程序使用这个包。

  • 我们可以通过在 R 控制台中输入 setwd("..") 来将工作目录设置在 ducksay 之外。这将把我们的工作目录移动到 ducksay 之上的一级目录。

  • 接下来,我们可以输入 file.create("greet.R") 来创建一个新文件。按照以下方式修改此文件:

    # Demonstrates using custom package  library(ducksay)  name  <-  readline("What's your name? ")  greeting  <-  ducksay(paste("hello,",  name))  cat(greeting) 
    

    注意到这个程序加载了 ducksay。然后,代码使用这个新的库。

  • 虽然这个包在我们的计算机上可以工作,因为我们是在本地开发的这个包,但其他人需要安装这个包。为此,可以使用以下命令之一:

    install.packages
    R CMD INSTALL 
    

    如前几节课所讨论的,顶级命令可以直接在 RStudio 中运行,并且是 R 本身构建的。另一个命令可以在计算机的终端中运行。它也是 R 中构建的。

  • 要安装我们的包,我们可以在控制台中运行以下命令:install.packages("ducksay_1.0.tar.gz")

  • 你可以使用 CRAN、GitHub 甚至电子邮件来共享你的代码。

总结

在本课中,你学习了如何在 R 中打包你的程序。具体来说,你学习了以下内容:

  • 包结构

  • devtools

  • 编写测试

  • 编写 R 代码

  • NAMESPACE

  • 测试代码

  • 编写文档

  • 打包包

  • 更新包

  • 使用和共享包

在本课程中,你学习了关于 R 和 R 编程的许多知识。你学习了如何表示数据、转换数据、应用函数、整理数据、可视化数据、测试程序和打包程序。总的来说,我们希望这些材料对你有所帮助。我们也希望你能将所学应用于世界上的伟大事业。

这就是 CS50 的 R 编程入门。

scratch

精灵

原文:cs50.harvard.edu/scratch/notes/1/

简介

  • Scratch 是一种由 麻省理工学院媒体实验室 的团队最初开发,现在由其自己的 Scratch 基金会 维护的基于视觉块状编程语言。

  • 通过在 Scratch 中组合“拼图块”,我们可以创建视觉故事、动画、游戏和其他程序。

  • 我们将学习和使用编程概念和思想,如函数、循环、条件和变量。

  • 尽管 Scratch 使用的是视觉块而不是文本代码,但其程序基于相同的基本思想,并使用相同的计算思维原则。

界面基础

  • 我们可以访问 Scratch 的网站,点击“开始创作”,在那里我们将看到一个类似这样的界面:

    Scratch 的网页用户界面,带有代码块、舞台和精灵的面板

    • 在左侧,我们有一个块库,我们可以将任何组合的块拖放到中间部分,称为块编辑器,我们将在这里构建我们的项目。

    • 在右上角,我们有舞台,我们的项目将在其中运行并展示给观看或使用它的人。

精灵

命名和定位

  • 当我们创建一个新的项目时,我们会看到 Scratch 猫角色,称为精灵,它只是一个可以出现在舞台上的对象。

  • 在舞台下方,我们看到我们项目中的所有精灵,目前我们只有一只猫,称为“Sprite1”。

  • 我们可以通过点击并拖动猫来在舞台上移动它。注意,当我们移动精灵时,精灵的位置值会发生变化。例如,当精灵移动到右上角时,x 值为 177,y 值为 42:

    舞台和精灵面板显示新位置

  • 结果表明,精灵位于舞台上的 x 和 y 坐标网格上,其中 x 值表示精灵向左或向右的距离,y 值表示精灵在舞台上的上下距离。

  • 舞台的完美中心是(0,0),正 x 值将精灵向右移动,负 x 值将精灵向左移动。同样,正 y 值将精灵向上移动,负 y 值将精灵向下移动。

  • 我们可以看到精灵的当前位置作为 x 和 y 值,但我们可以通过一小部分中的块来控制它们。

  • 我们也可以直接通过点击值并输入我们想要的值来更改精灵的 x 和 y 值。我们还可以点击精灵的名称,并将其更改为其他名称。这将帮助我们在我们开始向项目中添加更多精灵时跟踪我们的精灵。

  • 我们可以通过带有“显示”标签的切换来显示或隐藏每个精灵,并更改大小(以百分比表示),或方向,这将使猫旋转以面对一定数量的度数:

    带有名称、位置、显示、大小和方向的精灵控制

    • 当我们点击“方向”的值时,我们会看到一个可以旋转的小旋钮,这也会使舞台上的精灵旋转。

添加精灵

  • 让我们添加一个新的精灵。我们可以在精灵区域底部点击带有小加号的按钮,然后看到一些添加新精灵的选项:

    带有加号并标记为“选择一个精灵”的按钮

  • 现在,我们将使用主要的“选择一个精灵”按钮,我们将看到 Scratch 附带的大列表精灵,我们可以使用。我们可以点击类别或使用搜索框查找特定内容。

  • 我们将点击鱼,我们会看到两个精灵现在都在我们的舞台上。我们可以移动它们,使它们不重叠。

  • 我们还在底部区域有精灵,其中蓝色高亮的是选中的精灵,也是我们正在处理的精灵。我们可以点击它们中的每一个来更改它们的位置或大小,例如。

  • 我们可以右键点击(或按住控制键并点击)我们的鱼,然后看到一个带有一些选项的菜单。我们可以点击“复制”,现在我们将在舞台上有两条鱼:

    带有复制菜单项的鱼

    • 然后,我们可以移动它们,使它们不重叠:

      舞台上有猫和两条鱼在不同位置

  • 我们可以在Sprites中看到这个示例。

服装

  • 每个精灵都有一个服装,这仅仅是精灵的外观图像。

  • 在左上角,我们可以使用“服装”选项卡:

    鱼的服装选项卡

    • 我们会看到我们的鱼有四个服装,“fish-a”、“fish-b”、“fish-c”和“fish-d”,我们可以通过点击它们来选择不同的服装。
  • 对于猫,我们有两种不同的服装,它的腿处于不同的位置。通过在它们之间切换,我们可以让它看起来像是在走路。

  • 在服装编辑器中,我们甚至可以使用左下角的按钮添加新的服装,带有加号。我们可以从 Scratch 添加服装,或者使用中心工具绘制自己的服装。

  • 我们还可以通过选择它们并使用工具来更改它们的外观来编辑内置的服装。

  • 我们可以在Costumes中看到这个示例。

  • 最后,我们可以从我们的电脑上传自己的照片或图像作为服装使用。

声音

  • 我们可以使用顶部的“声音”选项卡为精灵添加声音:

    带有泡泡声和控制的“声音”选项卡

  • 我们看到我们的鱼有一个“bubbles”声音和一个“ocean wave”声音,如果我们选择精灵面板中的猫,我们会看到它只有一个声音,一个“meow”声音。

  • 我们可以更改内置的声音,或者录制或上传自己的声音。

背景

  • 我们的舞台有一个纯白色背景,因此我们可以点击右下角的按钮来选择一个新的背景:

    选择背景按钮

  • 现在我们将看到许多不同的背景,我们将使用水下背景来制作我们的鱼。

  • 但我们的猫现在位置不正确,因此我们可以在精灵面板中点击它,并使用垃圾桶图标来移除它。

  • 我们可以通过将旋转更改为负 90 度来翻转我们的黄色和绿色鱼,但这会使我们的鱼颠倒过来。结果是,我们可以将旋转样式从全方位(左边的圆圈)更改为左右(中心的三角形):

    鱼面向左,左右旋转被选中

    • 现在我们的精灵只会面向左边或右边。
  • 在右上角,我们可以点击全屏图标来全屏查看我们的舞台和精灵。

  • 我们可以在背景中看到这个例子。

保存

  • 要保存我们的项目以便我们以后可以保留和使用它,我们可以使用左上角的文件菜单来保存:

    带有保存选项的文件菜单

    • 保存我们的项目后,我们可以通过使用同一菜单中的“从您的电脑加载”选项来稍后加载它。
  • 我们还可以通过右上角的“加入 Scratch”按钮在 Scratch 上创建一个账户,这将把我们的项目保存到 Scratch 网站上。这也会让我们轻松与他人分享我们的项目。

下次

  • 下次,我们将开始使用这些代码块来编程我们的精灵执行不同的动作,甚至根据人的输入创建交互式故事和游戏。

函数

原文:cs50.harvard.edu/scratch/notes/2/

上次

  • 上次,我们介绍了舞台和精灵,包括服装、声音和背景。但我们通过点击和拖动等方式手动进行了所有更改。

  • 我们将打开一个新的 Scratch 项目,并回忆起我们可以通过点击和拖动,或者更改其 x 和 y 坐标的值来在舞台上移动我们的猫。

函数

  • 在 Scratch 界面的左侧,我们将看到积木块。

  • 在 Scratch 的上下文中,函数是指执行某些任务的积木块。

  • 例如,第一个积木块说“移动 10 步”,我们可以通过将其从积木块库拖到左侧,拖到项目中心的编辑部分来使用它。

    代码编辑器中心移动 10 步积木块

  • 现在,我们已经添加了我们的第一个函数,如果我们点击这个积木块,我们会看到我们的猫会稍微向右移动,并且它的位置 x 值也更新了。

  • 我们可以在移动中看到这个例子。

输入

  • 注意,在积木块中有一个值为 10 的区域,我们可以更改它。这个值被称为函数的输入,或者函数可以用来改变其行为的信息。

  • 对于移动积木块,输入是一个表示移动步数的数字。我们可以将其更改为移动我们的猫 30 步,例如:

     move (30) steps 
    
  • 我们还可以添加其他积木块,例如:

     turn right (15) degrees 
    
  • 我们可以在移动和转向中看到这个例子。

脚本

  • 我们可能希望我们的积木块可以组合,以便可以按照多个指令的顺序执行。

  • 我们可以通过将其中一个积木块拖向另一个积木块来将我们的两个积木块拼接在一起,当它们靠近时,我们会看到一个高亮显示的区域:

    当两个积木块拖动靠近时高亮显示的区域

    • 释放鼠标后,我们会看到它们拼接在一起。
  • 现在,我们的积木块堆叠可以被称为脚本,当我们点击其中一个积木块时,所有的积木块将按顺序从上到下运行:

     move (30) steps
      turn right (15) degrees 
    
    • 我们可以连续点击这个积木块堆叠,它们会使我们的猫沿圆形移动。
  • 我们可以使用许多不同类别的积木块来编写我们的脚本。我们也可以尝试这个积木块:

     go to (random position v) 
    
  • 如果我们想要删除一个积木块,我们可以按住控制键并点击,或者右键点击,并选择“删除积木块”选项。我们还可以将积木块拖回积木块库,如果我们在该区域内释放它,它就会消失。

四处走动

  • 我们将删除我们的猫,并为我们的下一个程序选择一个新的精灵,滑行,其中我们的精灵将在舞台上移动。

  • 让我们使用刺猬,我们首先使用“转到”积木块确保我们从左上角开始:

     go to x: (-180) y: (120) 
    
  • 在之后,我们将使用另一个积木块将我们的刺猬移动到右上角:

     go to x: (-180) y: (120)
      go to x: (180) y: (120) 
    
  • 然后我们想要移动到右下角:

     go to x: (-180) y: (120)
      go to x: (180) y: (120)
      go to x: (180) y: (-120) 
    
  • 最后,左下角,两个值都是负数:

     go to x: (-180) y: (120)
      go to x: (180) y: (120)
      go to x: (180) y: (-120)
      go to x: (-180) y: (-120) 
    
  • 当我们点击这个积木堆时,我们的刺猬似乎立即跳到了左下角。实际上,我们的计算机运行程序非常快,所以刺猬移动得如此之快,我们只看到了最终的位置。

  • 我们可以使用一个不同的积木,称为“滑动”,在一段时间内移动。我们将把底部的三个“转到”积木从我们的脚本中拖出来,因为我们仍然想在顶部左边立即开始,并像之前一样为其他三个位置添加“滑动”积木:

     go to x: (-180) y: (120)
      glide (1) secs to x: (180) y: (120)
      glide (1) secs to x: (180) y: (-120)
      glide (1) secs to x: (-180) y: (-120) 
    
  • 现在,当我们再次通过点击积木堆来运行我们的脚本时,我们看到我们的刺猬像我们预期的那样移动。我们可以再添加一个滑动,这样它就会回到原始位置:

     go to x: (-180) y: (120)
      glide (1) secs to x: (180) y: (120)
      glide (1) secs to x: (180) y: (-120)
      glide (1) secs to x: (-180) y: (-120)
      glide (1) secs to x: (-180) y: (120) 
    

注释

  • 我们的项目变得有些复杂,所以我们可以使用注释,或者对我们试图做什么的简短描述,作为我们自己的提醒或他人理解我们项目的指南。

  • 我们可以通过控制点击或右键点击我们的积木堆,并使用“添加注释”来写注释,就像给我们自己的笔记,这不会影响我们程序的运行:

     go to x: (-180) y: (120) // These blocks move the sprite in a rectangle.
      glide (1) secs to x: (180) y: (120)
      glide (1) secs to x: (180) y: (-120)
      glide (1) secs to x: (-180) y: (-120)
      glide (1) secs to x: (-180) y: (120) 
    
  • 我们可以使用其他动作积木来改变我们精灵的方向,并以其他方式移动。

外观

  • 现在,我们将点击左下角的“外观”类别积木,我们将拖走我们的其他积木,并拖入一个“说 Hello!”积木,用于Say

     say [Hello!] 
    
  • 现在,当我们点击这个积木时,一个话泡出现在我们的精灵旁边。

  • 让我们再添加一个积木来告别:

     say [Hello!]
      say [Goodbye!] 
    
  • 但是当我们点击这个积木堆时,我们遇到了一个类似的错误,或者程序的问题:Scratch 运行这些积木如此之快,以至于我们没有时间看到“Hello!”,因为它立即变成了“Goodbye!”。

  • 我们将用另一种类型的积木替换我们的积木:

     say [Hello!] for (2) seconds
      say [Goodbye!] for (2) seconds 
    
    • 现在,我们有时间查看我们刺猬发送的每条消息。

服装

  • “外观”积木也可以改变我们精灵的服装,所以我们将删除我们的刺猬精灵并创建一个新的:这次是一个熊,在Bear 1

  • 结果表明,熊已经有了两种服装可供选择,“bear-a”和“bear-b”。

  • 我们可以从使用积木选择我们熊的第一个服装开始,并将其放置在舞台的左边:

     switch costume to (bear-a v)
      go to x: (-120) y: (-50) 
    
  • 我们将添加积木使我们的熊在舞台上移动,并在之后站立起来:

     glide (2) secs to x: (120) y: (-50)
      switch costume to (bear-b v) 
    
  • 我们可以尝试点击我们的四个积木堆,看看这个动作会发生。

  • 现在,让我们通过点击底部右边的舞台面板中的“选择背景”按钮,选择“森林”和“树林和长椅”作为新的背景。

  • 我们可以使用一个积木来确保我们的故事总是以“森林”背景开始:

     switch backdrop to (Forest v)
      go to x: (-120) y: (-50)
      switch costume to (bear-a v) 
    
    • 我们还会告诉我们的熊从左边开始,并切换其服装,使其不站立。

    • 这三个积木都会运行得非常快,所以它们看起来好像同时发生,但实际上它们仍然一个接一个地运行。在这种情况下,由于它们都运行得如此之快,它们的顺序并不重要。

  • 现在,我们将用“滑动”方块告诉我们的熊像之前一样“走过”舞台:

     glide (3) secs to x: (300) y: (-50)
      switch backdrop to (Woods And Bench v) 
    
    • 使用较大的 x 值,我们的熊将走出舞台的右侧。

    • 一旦它这样做,我们的背景将改变为另一个,就像我们的熊到达了一个新的区域。

  • 然后,我们将让我们的熊“走进”舞台,从舞台左侧开始,停在中间:

     go to x: (-300) y: (-50)
      glide (3) secs to x: (0) y: (-50) 
    
  • 现在,我们有一堆七个方块,当点击时,用我们的熊讲述一个故事。如果某些事情没有按预期工作,我们可能想尝试为熊的位置或滑过舞台所需的时间尝试不同的值。

  • 还有两个可能有用的方块,“显示”和“隐藏”,可以使我们的精灵出现或消失:

     show
    
      hide 
    

声音,控制

  • “声音”类别的方块可以播放声音:

     play sound (pop v) until done 
    
  • 另一个有用的方块位于“控制”类别中,称为“等待”:

     wait (1) seconds 
    
    • 这将告诉我们的精灵暂停并什么都不做,这样我们就可以控制项目的节奏。
  • 我们将删除我们的熊,并将背景改回默认的白色背景。

  • 我们将添加一个新的精灵,鸭子,并使用我们看到的方块来玩捉迷藏:

     hide
      wait (1) seconds
      show
      play sound (duck v) until done 
    
    • 现在,我们的鸭子将消失,然后再次出现并播放声音。
  • 结果表明,我们可以录制自己的声音,从我们的电脑上传声音,甚至播放音符。

扩展

  • 我们将删除我们的鸭子精灵,再次选择猫。在 Scratch 界面的左下角,我们有一个带有方块和加号的蓝色图标,用于添加扩展,或更多类别的方块:

    带有方块和加号的蓝色图标

音乐

  • 我们将开始尝试音乐,通过尝试用这个方块播放音符:

     play note (60) for (0.25) beats 
    
    • 数字 60 对应某个音符或声音,0.25 拍表示它将播放多长时间。

    • 我们可以点击数字 60,这将显示一个钢琴键盘,当我们点击每个键时,我们可以看到音符编号的变化并听到它将听起来像什么。

  • 我们将使用这些方块中的八个,并按顺序播放键盘上的所有白键:

     play note (60) for (0.25) beats
      play note (62) for (0.25) beats
      play note (64) for (0.25) beats
      play note (65) for (0.25) beats
      play note (67) for (0.25) beats
      play note (69) for (0.25) beats
      play note (71) for (0.25) beats
      play note (72) for (0.25) beats 
    
    • 现在,当我们点击这个方块堆时,我们将听到一个音阶被演奏。
  • 我们还可以更改乐器,使音符听起来不同:

     set instrument to (\(4\) Guitar v) 
    
    • 我们将这个方块拖到堆栈的顶部,以便它在音符播放之前运行。
  • 我们还可以通过改变节奏来更快或更慢地播放我们的音符:

     set tempo to (80) 
    
    • 一个更大的数字,如 80,将使音符播放得更快,而一个较小的数字,如 40,将使音符播放得更慢。

  • 我们将尝试另一个扩展,笔扩展。有了这个,我们可以在舞台上移动我们的精灵,并在舞台上“绘制”时虚拟放下笔。让我们看看的例子。

  • 我们将首先放下笔,移动 30 步,然后拾起笔:

     pen down
      move (30) steps
      pen up 
    
    • 我们可以点击这个方块堆,我们的猫将继续移动 30 步并在移动时绘制线条。
  • 当我们“拿起”笔时,我们的精灵在移动时将不再绘制。我们可以让我们的猫画一个圆圈:

     pen down
      move (30) steps
      turn right (15) degrees
      pen up 
    
    • 现在我们点击这个积木堆时,我们的猫会移动一点,旋转,并画一个圆圈。

下次

  • 通过使用运动、外观、声音和控制类别中的积木,以及其他来自扩展的类型,我们可以用 Scratch 创建各种故事和程序。

  • 下次,我们将看到如何使用更多类型的积木,结合我们所学的内容,将我们的项目推进得更远。

事件

原文:cs50.harvard.edu/scratch/notes/3/

上次

  • 上次,我们通过组装堆叠的块来组合指令序列或函数。

  • 我们使用函数在舞台上移动我们的角色或精灵,绘制,播放声音等。

事件

  • 我们将从一个带有猫的新程序开始,并从一个我们之前见过的块开始:

     say [Hello!] for (2) seconds 
    
    • 现在,我们可以让我们的猫每次点击块时都这样做。
  • 但我们可能想让我们的猫对我们或另一个精灵做出反应。

  • 我们将添加一个恐龙精灵,并让它面对我们的两个精灵:

    向左旋转的恐龙,旁边是猫

  • 我们还将为恐龙添加相同的块,但现在如果我们想让我们的猫和恐龙都说你好,我们必须非常快速地分别点击它们的这些块。

  • 使用多个脚本和精灵,我们可能无法快速完成。如果能通过点击一个按钮来启动我们的项目,并自动运行所有脚本会更好。

  • 事实证明,Scratch 在舞台的左上角有一个看起来像绿色标志的按钮,可以启动我们的项目。但现在点击它没有任何反应。

  • 我们需要在事件类别下添加一个新的块类别。在编程中,事件只是在我们程序中发生的事情,我们的代码可以对其做出响应。

  • 因此,我们将拖出一个名为“当标志被点击时”的事件块,并将我们的“说你好”块附加到它上面:

     when green flag clicked
      say [Hello!] for (2) seconds 
    
    • 现在,我们的代码已附加到标志被点击的事件。当我们点击绿色标志时,我们的恐龙说你好。

    • 由于事件是触发我们代码其余部分运行的原因,因此无法将任何内容附加到此脚本的开始处。

  • 我们可以选择猫精灵,并通过在其脚本开头添加一个“当标志被点击”块来执行相同操作。现在,当我们点击绿色标志时,我们的两个精灵都说你好。

  • 事实上,现在别人可以通过点击绿色标志来运行我们的项目当标志被点击,而不必知道块的外观以及要点击哪个。

当精灵被点击

  • 我们将移除我们的猫和恐龙,并为我们的下一个示例添加一只鸭子当精灵被点击

  • 现在,我们将使用一个名为“当这个精灵被点击时”的块,当我们的精灵被点击时,下面的代码将运行:

     when this sprite clicked
      go to (random position v) 
    
    • 现在,当我们点击舞台上的鸭子时,它会移动到随机位置。我们也可以用“滑动”块替换“前往”块,使它移动得更平滑。
  • 注意,当我们点击舞台上的精灵时,脚本也会在运行时亮起:

    黄色光环中勾勒出的鸭子块

背景选择器

  • 现在,我们可以构建一个像计算机程序中的按钮一样工作的东西,当按下按钮时会发生某些事情,在背景选择器

  • 我们将添加一些背景到我们的项目中,并添加一个名为 Button2 的精灵。我们将按钮拖到屏幕的左侧,并在其服装中添加一些文本:

    带文本的北极按钮

  • 现在,我们将回到代码选项卡,并将事件和外观类别中的积木组合起来:

     when this sprite clicked
      switch backdrop to (Arctic v) 
    
    • 然后,当我们点击这个按钮时,背景将会改变。
  • 我们可以右键点击,或者控制点击精灵,并选择“复制”项来复制它。我们将其中一个重命名为“北极按钮”,另一个重命名为“丛林按钮”。然后,我们可以将“丛林按钮”的服装中的文本改为“丛林”。

  • 注意,代码编辑器的右上角将有一个当前精灵的略透明版本,这样我们就可以知道我们在更改哪个脚本。我们将更改代码以将此按钮的背景切换到“丛林”:

     when this sprite clicked
      switch backdrop to (Jungle v) 
    
  • 我们将“丛林按钮”拖到左侧,在“北极按钮”下方,并为名为“水下按钮”的按钮重复此过程。该按钮的积木将是:

     when this sprite clicked
      switch backdrop to (Underwater 1 v) 
    
  • 我们将按钮像这样堆叠在舞台上:

    堆叠在舞台上的按钮,标签为北极、丛林、水下

  • 现在,任何使用我们项目的人都可以与之互动,将舞台改变成他们喜欢的样子。

鼓组

  • 我们将我们的背景改回普通的白色背景,并添加一些新的精灵。

  • 我们将选择几个乐器,“鼓-军鼓”,“康加鼓”和“钹”,并将它们排列在我们的舞台上的鼓组

  • 对于它们中的每一个,我们将在事件类别中添加“当这个精灵被点击”积木,并在声音类别中添加一个“播放声音”积木:

     when this sprite clicked
      play sound (tap snare v) until done 
    
    • 我们将对每个乐器都这样做,每个乐器都有与之匹配的不同声音。
  • 现在,我们有一个“鼓组”,我们可以通过点击这些精灵中的任何一个来演奏。

  • 回想一下,点击是一个事件,我们的代码正在对这些事件做出响应。

游泳的鱼

  • 我们将移除鼓,并添加一条鱼。我们将使用“当按键按下”积木为它在游泳的鱼中启动一个新的脚本:

     when [space v] key pressed
      play sound (bubbles v) until done 
    
    • 现在,当我们按下键盘上的空格键时,我们的鱼将会播放声音。
  • 我们可以点击下拉菜单来自定义“当按键按下”积木以响应其他按键。

  • 我们将其改为:

     when [right arrow v] key pressed
      change x by (10) 
    
    • 我们将使用“移动”积木,当按下右箭头键时,将我们的精灵位置的 x 值移动 10 步。
  • 我们将右键点击,或者控制点击这个脚本,并选择“复制”选项来复制这两个积木。我们将点击将其放置在鱼精灵的代码编辑器中,并将新的脚本改为响应左箭头:

     when [left arrow v] key pressed
      change x by (-10) 
    
  • 但是我们的鱼是向后移动的,所以我们将首先添加积木来确保当按下右箭头键时它面向右边:

     when [right arrow v] key pressed
      point in direction (90)
      change x by (10) 
    
    • 回想一下,我们可以使用精灵面板中的旋钮来检查用于方向值的数字是多少。
  • 接下来,我们确保当按下左箭头键时它面向左边:

     when [left arrow v] key pressed
      point in direction (-90)
      change x by (-10) 
    
    • 我们一开始的鱼是倒置的,所以我们需要在精灵面板中将旋转样式设置为“左右”。我们也可以使用一个“设置旋转样式”的积木来帮我们完成这个操作。
  • 我们将重复这个操作,以便我们的鱼可以向上移动……

     when [up arrow v] key pressed
      change y by (10) 
    
  • …以及向下:

     when [down arrow v] key pressed
      change y by (-10) 
    
  • 现在,我们有了编程精灵在舞台上移动的能力,以响应按键。

改变大小

  • 我们将添加几个额外的脚本,当按下数字时改变鱼的大小:

     when [1 v] key pressed
      set size to (50) %
    
      when [2 v] key pressed
      set size to (100) %
    
      when [3 v] key pressed
      set size to (200) % 
    
    • 使用这些积木,我们可以像之前一样移动我们的鱼在舞台周围,通过按 1、2 或 3 键来改变它的大小或小。

响度

  • 我们将删除我们的鱼,并将背景改回纯白色背景,并为Balloon添加一个新的气球精灵。

  • 让我们尝试使用事件类别中的另一个积木,“当响度大于”。我们的浏览器可能会要求我们允许使用麦克风,因为 Scratch 将会监听我们的麦克风。

  • 我们将拖出这个积木,并添加一个积木来改变气球的大小,当 Scratch 听到的响度大于 30 时:

     when [loudness v] > (30)
      change size by (10) 
    
    • 响度值为 0 就像静音一样,100 将会非常非常响,所以我们可以尝试不同的数字来设置 Scratch 要监听的响度级别。

    • 然后,下面的代码将会响应。现在,当我们为麦克风制造噪音以便它能够听到时,我们的气球会越来越大。

计时器

  • 我们也可以更改积木下拉菜单中的值,从“响度”改为“计时器”:

     when [timer v] > (30) 
    
    • 结果表明,当我们通过点击标志开始我们的项目时,有一个计时器在跟踪已经过去的时间。

    • 因此,这个积木会在计时器达到特定数字时执行一些代码。

  • 我们将把我们的猫和恐龙放回Timer,并且对于我们的猫,我们将使用之前的相同脚本:

     when green flag clicked
      say [Hello!] for (2) seconds 
    
  • 至于我们的恐龙,我们将让它点击标志后等待两秒钟:

     when [timer v] > (2)
      say [Hello!] for (2) seconds 
    
  • 现在,当我们点击绿色标志时,我们的猫会先说你好,然后我们的恐龙会等待后再说你好。

  • 每次我们通过点击绿色标志开始我们的项目时,计时器都会重置为 0,并计算项目开始或停止再次之前的秒数。

当背景切换时

  • 还有其他的事件,比如这个积木:

     when backdrop switches to ( v) 
    
    • 现在,如果背景切换,我们可以让一个精灵执行一些动作。
  • 通过使用这些事件积木,我们可以使我们的 Scratch 项目交互式,并响应各种事件,如点击或按键。

原文:cs50.harvard.edu/scratch/notes/4/

上次

  • 上次,我们看了一下事件,我们的代码可以响应发生的事情,比如点击绿色标志。

  • 这次,我们将更仔细地看看函数,或者我们的代码,它对这些事件做出响应。

  • 回想一下,我们的一些函数可以接受一个或多个输入,或者某种类型的信息,这些信息进入椭圆形:

     move (10) steps 
    
    • 例如,这个“移动”块有一个值为 10 的椭圆形,告诉我们的精灵移动 10 步。

    • 我们可以改变这个值,即实际的数字,使其更大或更小。

  • 对于其他块,如“说”块,我们也可以使用单词作为输入。

  • 结果表明,Scratch 有一些自身就是值的块,我们可以使用。在“运动”类别块的底部,我们看到一些椭圆形的块:

     x position
      y position
      direction 
    
    • 由于它们不是矩形的,所以我们不能像我们迄今为止使用的矩形块那样堆叠它们。

    • 相反,我们可以将这些块放在其他块的椭圆形中,将它们用作输入值。

位置

  • 我们将告诉我们的精灵说它的 x 位置,或者它相对于舞台的左右距离,持续两秒钟:

     when green flag clicked
      say (x position) for (2) seconds 
    
    • 现在,当我们点击标志时,我们的猫会说出它的当前 x 位置。如果我们移动我们的猫在舞台上,当我们再次点击标志时,我们可以看到它说了一个不同的数字。
  • 我们将添加另一个块,这样我们就可以看到我们的猫的 x 位置和 y 位置:

     when green flag clicked
      say (x position) for (2) seconds
      say (y position) for (2) seconds 
    

操作符

  • 操作符是另一类块,也是编程中更一般的概念。操作符接受值作为输入,并产生一个新的值。

  • 例如,一个操作符是“+”块:

     () + () 
    
    • 这个操作符将取两个值,比如 1 和 2,并将它们相加。
  • 我们可以通过以下方式看到这一点:

     when green flag clicked
      say ((1) + (2)) for (2) seconds 
    
    • 注意,我们的绿色“+”操作符块将根据其自己的输入(3)计算一个值,然后这个值将用作我们猫的“说”块的值。
  • 我们可以使用另一个“连接”块,将两个单词或字符放在一起:

     when green flag clicked
      say (join (x position) (y position)) for (2) seconds 
    
    • 现在,我们可以在同一个“说”块中同时说出 x 位置和 y 位置,因为“连接”块会帮我们把它们放在一起。
  • 但当我们点击标志时,位置会相加,就像“-161-13”。

  • 我们将更改背景为“Xy-grid”,这样我们就可以可视化我们的猫的位置,猫确实在-161 和-13 的位置。但我们要用逗号分隔我们的值,这样我们就可以更容易地看到这一点。

  • 我们需要另一个“连接”块,将 x 位置与用逗号和空格连接的 y 位置连接起来:

     when green flag clicked
      say (join (x position) (join [, ] (y position))) for (2) seconds 
    
    • 因此,所有这些块将 x 位置与逗号、空格结合,然后是 y 位置。
  • 我们可以添加另一个“连接”块来形成一个完整的句子:

     when green flag clicked
      say (join [I am at] (join (x position) (join [, ] (y position)))) for (2) seconds 
    
  • 现在,我们已经创建了位置示例。

  • 我们还可以使用“方向”块让我们的猫说出它面对的方向。

按大小移动

  • 在外观类别块的底部,我们看到一些更多的椭圆形块,这意味着我们可以将它们用作输入。

  • 其中有一个叫做“大小”,它将仅仅表示我们的精灵有多大或多小。

  • 让我们以刺猬作为我们的精灵,我们将使用块使它在每次按下右箭头键时移动 10 步,在Walking Hedgehog中:

     when [right arrow v] key pressed
      move (10) steps 
    
  • 但如果我们改变刺猬的大小,我们希望它每次移动时更多或更少,这取决于它的大小。

  • 我们将把“大小”块拖进来作为值:

     when [right arrow v] key pressed
      move (size) steps 
    
    • 但由于我们的刺猬从 100%大小开始,它每次移动 100 步。
  • 我们可以使用运算符类别中的“/”块,它执行除法操作。所以我们将它拖进来,并将我们的“大小”块也拖进去:

     when [right arrow v] key pressed
      move ((size) / (10)) steps 
    
    • 我们将大小除以 10,所以在 100%大小的情况下,我们的刺猬将移动 10 步,但在 50%大小的情况下,它将只移动 5 步。

随机选择

  • 运算符类别中另一个块是“随机选择”,它会为我们选择一个随机数。

  • 我们将通过告诉我们的刺猬每次点击标志时指向一个随机方向来尝试它,在Rotating Hedgehog中:

     when green flag clicked
      point in direction (pick random (0) to (90)) 
    
    • 现在,每次我们按下绿色标志,我们的刺猬会做一些令人惊讶的事情,它不一定会每次都做同样的事情。

计时器,取整

  • 在块中,感知类别中我们看到另一个值,称为“计时器”。计时器的值将是我们开始项目以来的秒数,所以我们将让刺猬每次点击时说出它,在Timing Hedgehog中:

     when this sprite clicked
      say (timer) for (2) seconds 
    
    • 现在,点击绿色标志后,我们可以点击刺猬来查看秒数,比如 4.45 或 9.90。
  • 我们不想看到秒数的这么多细节,所以我们将寻找运算符类别中的“取整”块。取整块将接受一个数字作为输入,并将其四舍五入到整数。

  • 因此,我们可以将我们的“计时器”块拖入一个“取整”块中,现在我们的刺猬将说出整数的秒数:

     when this sprite clicked
      say (round (timer)) for (2) seconds 
    

感知

  • 在感知部分,我们看到另一个块,“询问你的名字?并等待”。椭圆形表示函数块的输入,在这种情况下,“你的名字是什么?”告诉块使用这个作为问题。

  • 结果,这个块还有一个输出,或者返回值,在我们运行它后会返回。

  • 我们可以看到,在这个块下面有一个椭圆形的“答案”块,它将显示用户对这个问题输入的任何响应的值:

    感知类别中的询问块和答案块

  • 让我们通过构建一个名为Hello的程序来看看:

     when green flag clicked
      ask [What's your name?] and wait
      say (answer) for (2) seconds 
    
    • 现在,当我们运行我们的程序时,我们的猫会提出问题,并等待我们输入响应后继续:

      舞台上的猫询问你的名字并带有输入框

  • 我们可以用“加入”模块让我们的猫变得更有亲和力:

     when green flag clicked
      ask [What's your name?] and wait
      say (join [Hello,] (answer)) for (2) seconds 
    
  • 让我们把问题改为“走了多少步?”:

     when green flag clicked
      ask [How many steps?] and wait
      move (answer) steps 
    
    • 我们还将使用一个“移动”模块,并在其中使用“答案”模块作为输入。因此,我们的猫将移动我们作为问题的答案输入的步数。
  • 我们也可以通过询问去哪里?中的 x 值和 y 值来告诉我们的猫去一个特定的位置:

     when green flag clicked
      ask [Pick x] and wait
      set x to (answer)
      ask [Pick y] and wait
      set y to (answer) 
    
    • 注意,我们可以提出多个问题,而“答案”模块将具有我们为最近一个问题输入的任何值。

更改服装

  • 我们现在暂时移除我们的猫,再次添加熊。我们可以在服装标签中重命名它的服装:

    熊的服装标签,服装命名为 4 和 2

    • 我们将把熊的四条腿都穿上的服装称为“4”,而另一条两条腿的服装称为“2”。
  • 现在,我们可以构建一个程序,有多少条腿?,来询问我们使用哪个服装:

     when green flag clicked
      ask [How many legs?] and wait
      switch costume to (answer) 
    
    • 结果表明,“切换服装”模块也可以接受一个椭圆形模块作为输入,因此现在我们的答案将被用作我们熊将切换到的服装名称的值。

更改背景图

  • 我们可以用背景图来尝试这个功能。我们将添加“彩色城市”和“夜晚城市”作为我们的背景图,通过点击右下角的舞台面板,我们可以打开背景图标签来重命名它们:

    背景图标签,背景图重命名为白天和夜晚

    • 我们将把白天城市的背景图称为“白天”,而夜晚的背景图称为“夜晚”。
  • 什么时间?,我们将创建熊的脚本:

     when green flag clicked
      ask [What time?] and wait
      switch backdrop to (answer) 
    
    • 现在,当我们回答“白天”或“夜晚”时,背景图将改变。
  • 通过这些允许用户回答问题的模块,以及我们的代码将他们的答案作为函数输入,我们可以给用户更多的控制权,并使我们的项目变得更加有趣。

条件

原文:cs50.harvard.edu/scratch/notes/5/

上次

  • 上次,我们查看了一些值,这些是我们可以在 Scratch 程序中使用的信息。

  • 今天,我们将通过提问来使用值做出决定,并根据那些答案决定要做什么。例如,我们可能会在出门前问,“外面冷吗?”如果答案是肯定的,我们可能会穿上夹克。

触碰鼠标指针

  • 我们将从告诉我们的猫在按下空格键时播放声音的脚本开始喵喵

     when [space v] key pressed
      play sound (Meow v) until done 
    
  • 在积木的“控制”部分,有一个名为“如果”的积木,我们可以使用它,其中有一个六边形区域,还有一个地方可以嵌套额外的积木:

     if <> then 
    
  • 六边形代表我们想要提出的问题,这个问题可以用“是”或“否”来回答,或者等价地,用“真”或“假”来回答。

  • 在积木的“感应”部分,我们看到一些六边形形状的积木。实际上,我们可以提出的问题被称为布尔表达式,或者描述某物可以是真(用“是”回答)或假(用“否”回答)的一种方式。

  • 我们将把“触碰鼠标指针?”布尔表达式积木拖入“如果”积木的六边形中:

     when [space v] key pressed
      if <touching (mouse pointer v)?> then
      play sound (Meow v) until done 
    
    • 我们还将放置我们的“播放声音”积木。

    • 现在,当我们按下键盘上的空格键时,如果我们的鼠标指针或屏幕上的光标触碰到舞台上的精灵,我们的猫才会播放声音。

  • 我们还可以检查我们的精灵是否在猫和气球中触碰到另一个精灵。我们将添加一个气球精灵,并在“触碰鼠标指针?”积木的下拉菜单中,我们现在可以将值更改为:

     when [space v] key pressed
      if <touching (Ballon1 v)?> then
      play sound (Meow v) until done 
    
    • 现在,我们的猫只有在我们将气球拖到舞台上的它那里时才会播放声音。

触碰颜色

  • “触碰颜色?”积木将允许我们检查我们的精灵是否触碰到特定颜色的任何东西。

  • 我们将保留我们的猫,并使用“冬季”背景。我们将在名为猫和树的程序中创建这个脚本:

     when [right arrow v] key pressed
      change x by (10)
      if <touching color [#165e0f] ?> then
      say (I found a tree!) for (2) seconds 
    
  • 我们将拖入这些积木中的每一个,对于“触碰颜色?”积木,我们可以点击颜色来使用不同的滑块选择颜色:

    触碰颜色积木中的颜色滑块

  • 但要精确地获取树的颜色可能有些困难,因此我们可以点击颜色滑块底部的吸管图标,并使用它来用鼠标选择舞台上的颜色:

    带有颜色选择器的舞台和树的绿色部分

  • 现在,当按下右箭头键时,我们的猫会向右移动。每次它向右移动时,它也会问自己是否触碰到绿色。最终,当它到达绿色的大树时,那个问题的答案将是“是”,它会说“我找到一棵树了!”。

大小

  • 我们将设置舞台为普通背景,并恢复气球。我们将使用这个脚本使气球在气球中生长:

     when [space v] key pressed
      change size by (10) 
    
  • 但我们想让我们的气球最终爆裂,实际上我们可以使用“=”运算符来比较两个数字。我们将使用一个“if”块,并在其中拖入这些块:

     when [space v] key pressed
      change size by (10)
      if <(size) = (200)> then 
    
    • 注意,“=”运算符的形状像一个六边形,所以它也是一个我们可以使用的布尔表达式,答案为“是”或“否”。

    • 现在,每次按下空格键时,我们会检查大小是否等于 200。

  • 如果大小是 200,我们可以让我们的气球精灵隐藏,并播放爆裂声音:

     when [space v] key pressed
      change size by (10)
      if <(size) = (200)> then
      hide
      play sound (Pop v) until done 
    
    • 现在,当我们的气球达到 200 大小时,它会消失并播放声音,就像它爆裂了一样。

如果...那么...否则

  • 我们还有运算符来检查一个数字是否大于或小于另一个数字。

  • 我们将在数字中使用鸭子精灵,并且我们首先会要求输入一个数字:

     when green flag clicked
      ask [Number:] and wait
      if <(answer) > (0)> then
      say [Positive] for (2) seconds 
    
    • 然后,我们可以检查那个问题的答案是否大于 0。如果是这样,那么这个数字将是一个正数,我们可以告诉用户它是正数。
  • 现在,我们能够在问题的答案是“是”时运行一些代码。但如果答案是“否”,我们可能还想做其他事情。实际上,在控制部分还有一个名为“if then else”的块:

     when green flag clicked
      ask [Number:] and wait
      if <(answer) > (0)> then
      say [Positive] for (2) seconds
      else
      say [Negative] for (2) seconds 
    
    • 底部的新部分,称为“else”,将包括另一个用于当问题的答案是“否”时运行的块堆。所以无论问题的答案是什么,我们都会运行两个块堆中的一个。

    • 当问题的答案是“否”时,我们有一个负数,所以我们的鸭子会说“负数”。

  • 但现在我们的程序中有一个错误,当 0 出现时,我们的鸭子会说“负数”,因为 0 不大于 0。我们可以在“else”部分添加另一个“if then else”块:

     when green flag clicked
      ask [Number:] and wait
      if <(answer) > (0)> then
      say [Positive] for (2) seconds
      else
      if <(answer) < (0)> then
      say [Negative] for (2) seconds
      else
      say [Zero] for (2) seconds 
    
    • 所以现在,如果数字不是大于零,我们会问另一个问题,如果它是小于零。如果是这样,我们可以说“负数”。否则,数字必须是 0。

刺猬赛跑

  • 让我们把刺猬添加到我们的舞台上,并编写一个程序来告诉我们我们有多快地将其移动到舞台的另一边,刺猬赛跑 1

  • 我们将从一组块开始,这些块将我们的刺猬带到舞台的左侧:

     when green flag clicked
      go to x: (-150) y: (-25) 
    
  • 当按下右箭头键时,我们还想让我们的刺猬移动 10 步:

     when [right arrow v] key pressed
      move (10) steps 
    
  • 当我们的刺猬到达舞台的边缘时,我们想让它说出计时器的值,或者自我们开始程序以来已经过去了多少秒。

     when [right arrow v] key pressed
      move (10) steps
      if <touching (edge v)?> then
      say (round (timer)) for (2) seconds 
    
    • 每次按下右箭头键时,我们的刺猬将移动 10 步,但也会检查它是否触碰到舞台的边缘。

    • 如果它是,我们将对计时器的值进行四舍五入,因为我们并不真的关心小数部分,只想得到整数的秒数。然后我们将在“say”块中使用这个值,以便在我们到达边缘时看到刺猬说出它。

  • 我们还可以在Hedgehog Race 2中添加更多问题:

     when [right arrow v] key pressed
      move (10) steps
      if <touching (edge v)?> then
      if <(timer) < (5)> then
      say [Fast!] for (2) seconds
      else
      say [Slow!] for (2) seconds 
    
    • 现在,我们有一个询问“计时器”值是否小于 5 的块,如果答案是肯定的,我们将说“快!”。否则,我们将说“慢!”。

刺猬迷宫

  • 我们将刺猬添加到我们的程序中,并使用Hedgehog Maze构建一个带有迷宫的游戏。

  • 我们将点击右下角的舞台面板,并使用背景部分在背景上绘制线条,就像迷宫一样:

    带有红色线条的背景编辑器

    • 我们将选择线条工具,将轮廓改为红色,并使用鼠标在背景周围绘制几条线。
  • 现在,我们将我们的刺猬大小改为 40,并添加块使其在按下右箭头键时向右移动:

     when [right arrow v] key pressed
      change x by (5) 
    
  • 我们还希望检查刺猬是否触碰到迷宫中的墙壁。因此,我们将使用“触碰颜色?”块:

     when [right arrow v] key pressed
      change x by (5)
      if <touching color [#ff0000] ?> then
      go to (random position v) 
    
    • 现在,每次我们的刺猬移动时,它都会检查是否接触到红色。如果答案是“是”,我们将让它移动到舞台上的一个随机位置。
  • 我们将右键单击或控制单击这个块堆,并选择“复制”选项,为左箭头键创建一个类似的堆:

     when [left arrow v] key pressed
      change x by (-5)
      if <touching color [#ff0000] ?> then
      go to (random position v) 
    
    • 注意,我们通过改变 x 值减去 5 来向左移动。
  • 我们将重复此操作,使用“改变 y”块来上下移动:

     when [up arrow v] key pressed
      change y by (5)
      if <touching color [#ff0000] ?> then
      go to (random position v)
    
      when [down arrow v] key pressed
      change y by (-5)
      if <touching color [#ff0000] ?> then
      go to (random position v) 
    
  • 现在,我们可以通过按箭头键来测试这个功能,每次我们的刺猬触碰到墙壁时,它都会消失然后重新出现。

  • 使用这些条件和布尔表达式的块,我们可以在程序内部根据问题的答案提出问题并做出决策。

循环

原文:cs50.harvard.edu/scratch/notes/6/

上次

  • 上次,我们结合了函数、值和条件的块,使我们的 Scratch 程序能够提出问题,并根据这些问题的答案做出决定。

  • 但每次我们运行代码时,无论是按绿色标志、按键还是点击,每个脚本都只能从上到下运行一次。

行走猫

  • 现在,我们将告诉我们的程序循环,或重复多次执行一些块。

  • 我们将以一个行走猫的例子开始,当点击绿色标志时,我们的猫将移动 10 步:

     when green flag clicked
      move (10) steps 
    
  • 我们将在块的“控制”部分使用“永远”块,使我们的猫反复移动:

     when green flag clicked
      forever
      move (10) steps 
    
    • 现在,我们的猫将移动,直到它到达边缘,在那里它不能再移动。它仍然会尝试移动。
  • 我们可以点击舞台顶部的停止标志,旁边是绿色标志,并将猫拖回舞台的左侧。

  • 我们将添加另一个块,告诉我们的猫在到达边缘时跳跃或转身:

     when green flag clicked
      forever
      move (10) steps
      if on edge, bounce 
    
    • 现在,我们的猫在边缘转身时是倒置的,但我们可以通过在精灵面板中的方向旋钮上点击并选择第二个选项来更改旋转样式:

      选择左右旋转风格的精灵方向旋钮

  • 然而,我们的猫的腿并没有移动,实际上我们可以制作一个动画,图像移动得足够快,从而产生运动的错觉。

  • 结果表明,我们的猫有两种服装,它的腿处于略微不同的位置。因此,我们可以使用“下一个服装”块在两者之间交替:

     when green flag clicked
      forever
      move (10) steps
      if on edge, bounce
      next costume 
    
  • 我们的猫看起来移动得有点快,所以我们将减慢它的移动速度:

     when green flag clicked
      forever
      move (10) steps
      if on edge, bounce
      next costume
      wait (0.1) seconds 
    
    • 现在,我们的猫将移动,如果它在边缘,则弹跳,切换到下一个服装,然后等待一秒钟的短暂时间。然后它会反复执行所有这些块,直到我们按下停止标志。

游泳鱼

  • 我们将在游泳鱼中使用“水下 1”背景,并让鱼指向鼠标光标:

     when green flag clicked
      forever
      point towards (mouse-pointer v) 
    
    • 通过使用“永远”块,在点击绿色标志后,鱼将始终指向鼠标,反复进行。
  • 我们也可以让鱼每次移动 5 步:

     when green flag clicked
      forever
      point towards (mouse-pointer v)
      move (5) steps 
    
  • 我们也可以用这个作为背景。我们可以在舞台面板中点击背景,并拖入这些块:

     when green flag clicked
      forever
      play sound (Ocean Wave v) until done 
    
    • 最初,“播放声音”块只有“砰”的声音,所以我们将使用“声音”选项卡将“海洋波浪”声音添加到我们的背景中。然后,我们可以使用“播放声音”块中的下拉菜单选择“海洋波浪”。

    • 现在,我们的背景将反复播放波浪声。

  • 现在,当我们点击绿色标志来运行我们的程序时,我们将有多个“永远”循环运行。我们的鱼将跟随鼠标指针,背景将连续播放波浪声。

  • 使用这些积木,我们可以在我们的程序中添加重复播放的音乐,让我们有一种总有什么事情在发生的感觉。

抚摸猫

  • 我们将移除我们的背景和鱼,并添加回猫,以抚摸猫

  • 现在,让我们告诉我们的猫,当鼠标太靠近时,它要发出“喵喵”声:

     when green flag clicked
      if <(distance to (mouse-pointer v)) < (100)> then
      play sound (Meow v) until done 
    
    • 我们可以添加一个条件来检查鼠标指针的距离。如果它小于 100 的值,我们的猫就会发出声音。

    • 注意,我们可以在积木的分类中找到“距离到”积木。

  • 但当我们运行我们的程序时,没有任何事情发生,即使我们移动鼠标指针靠近猫。结果是我们的积木堆只运行了一次,一旦我们点击绿色标志,它就会提出问题,但由于我们的鼠标指针离猫很远,所以什么也不做。

  • 通过添加“永远”积木并将我们的条件放在其中,我们可以不断地提出这个问题,并且每次我们的鼠标指针太靠近时都会播放声音:

     when green flag clicked
      forever
      if <(distance to (mouse-pointer v)) < (100)> then
      play sound (Meow v) until done 
    
  • 一个“永远”循环,它会在程序结束时重复运行,也被称为无限循环。

喵喵

  • 我们还可以使用“重复”积木来运行一些代码块特定次数:

     repeat (10) 
    
    • 注意,在这个积木的右下角有一个小箭头,表示里面的代码将会被重复执行。
  • 例如,如果我们想让我们的猫叫三次,我们可以使用:

     when green flag clicked
      play sound (Meow v) until done
      play sound (Meow v) until done
      play sound (Meow v) until done 
    
  • 但我们可以通过使用“重复”积木来改进我们程序的布局,喵喵,而不是反复使用相同的“播放声音”积木:

     when green flag clicked
      repeat (3)
      play sound (Meow v) until done 
    
    • 这更好,因为我们现在只使用两个积木,而不是像以前那样使用三个。如果我们想重复 30 次,我们只需要改变椭圆形中的数字,而不是拖出 27 个额外的“播放声音”积木。
  • 我们还可以在“重复”积木的椭圆形中使用其他值。例如,我们可以提出一个问题,并使用答案作为输入:

     when green flag clicked
      ask [Number:] and wait
      repeat (answer)
      play sound (Meow v) until done 
    
    • 现在,我们的程序用户可以控制我们的猫叫多少次。
  • 我们可以在另一个例子中尝试让我们的猫在圆形中移动,圆形

     when green flag clicked
      repeat (24)
      move (30) steps
      turn right (15) degrees 
    
    • 我们的猫将移动 30 步,轻微转向,然后重复 24 次,在圆形中移动自己。

    • 以前,我们需要自己一次又一次地点击我们的积木。现在,我们的猫能够非常快速地重复所有这些操作。

气球

  • 我们将在Balloon 1中使用循环让气球自己膨胀:

     when green flag clicked
      repeat (10)
      change size by (10)
      wait (0.2) seconds
      end
      hide
      play sound (Pop v) until done 
    
    • 现在,当我们点击绿色标志时,我们的气球将增加 10 个单位的大小,暂停一秒钟。它会这样做 10 次,直到它变得越来越大,然后消失并播放噼啪声。
  • 但当我们再次点击绿色标志时,什么也没有发生。气球仍然隐藏着,所以我们需要添加一些积木来再次显示它:

     when green flag clicked
      show
      set size to (100) %
      repeat (10)
      change size by (10)
      wait (0.2) seconds
      end
      hide
      play sound (Pop v) until done 
    
    • 我们还将使用“设置大小”积木将气球恢复到原始大小。现在,每次我们点击绿色标志,它都会重新出现。
  • 由于我们从 100 开始,并且增加 10 次共 10 次,气球将在 200 大小时爆裂。我们可以将循环重复的次数改为 15,现在气球将增长到 250 大小时才爆裂。

  • 结果我们发现可以在“控制”部分的另一个块中使用“重复直到”,这样我们就可以避免自己进行那个计算,在Balloon 2中:

     when green flag clicked
      show
      set size to (100) %
      repeat until <(size) = (200)>
      change size by (10)
      wait (0.2) seconds
      end
      hide
      play sound (Pop v) until done 
    
    • “重复直到”块是循环和条件的组合。它将反复运行,直到某个问题被回答为“是”,或者变为真。

    • 我们将在“运算符”部分使用六边形形状的“=”运算符来比较气球的大小,一旦它等于 250,我们的“重复直到”块将停止重复。

    • 条件成立后,我们其余的块将运行,隐藏我们的气球并播放爆裂声。

  • 通过使用“永远”块、“重复”块和“重复直到”块,我们能够在程序中创建循环,多次运行我们的代码,而无需我们一次次地点击。

变量

原文:cs50.harvard.edu/scratch/notes/7/

  • 到目前为止,我们使用的是程序中可以作为决策依据或作为函数输入的信息值。

  • 当这些值存储在我们的程序中以便我们稍后使用时,它们可以被称作变量

计数猫

  • 让我们构建计数猫的示例。

  • 我们将在代码块的“变量”部分查找,并看到这个标有“创建变量”的按钮:

    创建变量按钮

    • 当我们点击这个按钮时,会要求我们输入变量的名称,所以我们将输入“count”,因为我们想用它来存储计数。

    • 我们还可以决定这个变量是否对所有精灵可用或可更改,通常被称为全局变量。或者,我们也可以使这个变量仅对当前精灵可用,也称为局部变量。我们将为这个“count”变量选择“对所有精灵”。

  • 现在,我们看到了一个可用的椭圆形“count”代码块,它左侧有一个复选框。复选框会告诉 Scratch 在舞台的左上角显示变量的值,或者存储在变量中的内容。我们可以取消勾选,这样值就会在舞台上隐藏。

  • 我们可以为我们的猫添加计数点击次数的代码块:

     when this sprite clicked
      change [count v] by (1)
      say (count) for (1) seconds 
    
    • “改变”代码块会将“count”变量的值增加 1。

    • 然后,我们的猫会说出变量的值。

  • 我们可以添加另一个精灵,“Button2”,它将是一个我们可以用来将计数重置为 0 的按钮。我们将使用“外观”选项卡为我们的按钮添加文本,使其标有“重置”:

    黑色文本的按钮外观

  • 我们将为重置按钮添加这些代码块:

     when this sprite clicked
      set [count v] to (0) 
    
    • 现在,当这个按钮被按下时,0 的值将被放入“count”变量中,替换掉之前的内容。

追逐星星

  • 现在,我们可以制作一个游戏,让刺猬跟踪我们的分数,追逐星星

  • 我们将删除我们的精灵,并右键点击或按住控制键点击“count”变量,然后选择“删除”选项来将其也删除。

  • 我们将添加我们的刺猬,并告诉它指向并移动到鼠标指针的方向:

     when green flag clicked
      forever
      point towards (mouse-pointer v)
      move (5) steps 
    
  • 我们将添加另一个精灵,星星,每次刺猬触摸它时,它都会增加我们的分数。我们还会让它在被触摸后移动到随机位置:

     when green flag clicked
      forever
      if <touching (Hedgehog v) ?> then
      change [score v] by (1)
      go to (random position v) 
    
    • 首先,我们需要创建一个新的变量,我们将称之为“score”。

    • 我们将使用无限循环让星星不断检查它是否触摸到刺猬,每次触摸时,我们将分数增加 1。

    • 最后,我们将告诉我们的星星在被触摸后移动到随机位置。

  • 现在,我们有一个游戏,我们可以尝试用刺猬收集星星。

  • 当程序开始运行时,我们可能希望将分数重置为 0。我们将使用刺猬来实现这一点:

     when green flag clicked
      set [score v] to (0)
      forever
      point towards (mouse-pointer v)
      move (5) steps 
    
    • 注意,我们想要将“设置”块放在“无限循环”之外。这样,它只会被设置为 0 一次,之后由星星改变。
  • 我们可以让程序在获得一定数量的分数时改变背景并停止。

  • 首先,我们将点击右下角的舞台面板,并转到背景选项卡。然后点击左下角的带加号的按钮,选择“绘画”来创建我们自己的背景:

    背景选项卡中的绘画按钮

  • 我们将添加一个覆盖背景的绿色矩形和一些写着“你赢了!”的文本。我们将把背景的名字改为“Win”。

  • 现在,我们想要确保背景从纯白色背景“backdrop1”开始,所以我们将它添加到我们的刺猬上:

     when green flag clicked
      set [score v] to (0)
      switch backdrop to (backdrop1 v)
      forever
      point towards (mouse-pointer v)
      move (5) steps 
    
  • 然后,在我们的星星代码块中,我们希望在达到 10 分时改变背景并停止我们的程序:

     when green flag clicked
      forever
      if <touching (Hedgehog v) ?> then
      change [score v] by (1)
      if <(score) = (10)> then
      switch backdrop to (Win v)
      stop [all v]
      end
      go to (random position v) 
    
    • 在我们改变分数后,我们想要添加一个检查“分数”值的条件。如果它等于 10,那么我们将切换背景。我们还会在控制部分使用“停止”块,这将停止我们程序中的所有内容。

    • 回想一下,我们可以在运算符部分得到“=”块,在变量部分得到“分数”块。

弹跳球

  • 我们将移除这些精灵,并为我们的下一个示例选择另一个背景,“蓝天”,弹跳球

  • 我们将添加球精灵,并告诉它开始下落,或者向下移动,直到它到达地面:

     when green flag clicked
      forever
      change y by (-5)
      if <touching color [#663600] ?> then
      stop [all v] 
    
    • 我们将 y 位置减去 5,这使得球向下移动。

    • 然后,如果球接触到背景地面上的棕色,我们将停止我们的程序。我们将点击颜色,然后点击吸管来选择棕色。

    • 现在,当我们点击旗帜时,我们的球开始下落。我们可以将球拖回舞台顶部,再试一次。

  • 但这种下落并不非常逼真。一个真实的球最初下落得很慢,然后越来越快。

  • 让我们的球每次移动一个可变步数。我们将创建一个新的变量叫做“速度”,因为它将存储下落的速度,我们可以使这个变量只对“这个精灵”可用,因为其他精灵不需要使用它。

    • 现在,在我们的舞台左上角,我们看到变量被标记为“球:速度”,告诉我们它属于哪个精灵。
  • 对于球的代码,我们将在它下落时增加“速度”变量的值:

     when green flag clicked
      set [speed v] to (0)
      forever
      change y by (speed)
      change [speed v] by (-1)
      if <touching color [#663600] ?> then
      stop [all v] 
    
    • 首先,我们将“速度”设置为 0。然后,我们将改变球的位置 y 值,这个值最初将是 0。

    • 之后,我们将“速度”的值减去 1,这使得它更加负,因此我们的球会更快地向下移动舞台。

    • 我们的无限循环会一直重复运行,直到“停止所有”块告诉我们的程序停止。

  • 我们可以让球在接触地面后向上移动,也可以:

     when green flag clicked
      set [speed v] to (0)
      forever
      change y by (speed)
      change [speed v] by (-1)
      if <touching color [#663600] ?> then
      set [speed v] to ((speed) * (-1)) 
    
    • 我们可以通过乘以-1 来将我们的“速度”变量的值从负数变为正数。然后,球将开始在舞台上向上移动,但“速度”变量会再次每次减少 1。

    • 最终,球的“速度”将变为 0,然后变为负数,然后它将再次开始沿着舞台向下移动。

  • 我们还可以让球每次弹跳时稍微失去一点速度:

     if <touching color [#663600] ?> then
      set [speed v] to (((speed) * (-1)) - (2)) 
    
    • 当速度从负数变为正数时,我们也可以从它减去 2,这样新的速度总是比之前略小。

    • 最终,我们希望球停止移动,因此我们需要添加一些额外的代码来实现这一点。

  • 使用变量,我们可以近似弹跳球的速率,使其运动更加逼真。

气球

  • 让我们看看一个例子,用户可以在气球中控制气球的大小。

  • 我们有一个气球精灵,我们将创建一个新的变量叫做“空气”。在舞台上,我们可以右键点击,或者按住控制键点击变量,并改变读数的外观:

    带有正常读数、大读数、滑块的变量

    • 我们可以选择显示大读数,但暂时我们选择滑块。
  • 现在,用户可以移动滑块来改变变量的值。我们可以右键点击,或者按住控制键点击滑块,选择“更改滑块范围”,以便有一个最小值和最大值。为此,我们将最小值设置为 100,最大值设置为 200,因为大小应该在两个值之间。

  • 我们将在气球精灵中添加代码块,告诉它改变其大小:

     when green flag clicked
      forever
      set size to (air) % 
    
    • 现在,点击绿色标志后,气球将不断将其大小设置为“空气”变量的值,即使我们在舞台上改变滑块的值。
  • 通过这些想法,我们可以允许用户直接控制变量来与我们的项目或精灵交互。

螺旋

  • 让我们看看一个带有笔扩展的例子,螺旋

  • 我们会让猫沿着圆形移动:

     when green flag clicked
      go to x: (0) y: (0)
      point in direction (90)
      forever
      move (10) steps
      turn right (15) degrees 
    
    • 我们将从屏幕中心面向右开始移动和转向,以获得圆形移动的效果。
  • 我们将通过使用界面左下角的蓝色按钮来添加笔扩展。然后我们将擦除一切,放下笔开始绘制:

     when green flag clicked
      go to x: (0) y: (0)
      point in direction (90)
      erase all
      pen down
      forever
      move (10) steps
      turn right (15) degrees
      wait (0.05) seconds 
    
    • 我们还会让猫在每一步之间等待一小段时间,这样我们就可以看到它是如何画圆的。
  • 现在,让我们创建一个新的变量叫做“步骤”。我们首先将其设置为 0,并告诉猫按照变量的值移动:

     when green flag clicked
      go to x: (0) y: (0)
      point in direction (90)
      erase all
      pen down
      set [steps v] to (0)
      forever
      move (steps) steps
      change [steps v] by (0.5)
      turn right (15) degrees
      wait (0.05) seconds 
    
    • 注意,我们还在每次移动时稍微增加“步骤”变量的值,每次增加 0.5。因此,每次移动时,我们都会移动得更远。结果看起来像螺旋。
  • 使用循环、变量和笔,我们可以在程序中绘制一些有趣的图形。

  • 更普遍地,使用变量,我们可以在程序中跟踪信息,并在以后更改或使用它们,从而制作出比以前更强大、更精彩的电子游戏、动画和故事。

抽象

cs50.harvard.edu/scratch/notes/8/

  • 到目前为止,我们已经看到了 Scratch 的许多功能,包括让我们可以向项目中添加循环、变量和条件的块。

  • 现在,让我们考虑一下我们如何可能改进我们在 Scratch 项目中设计的块的方式。

恐龙游戏

  • 让我们从添加一个恐龙精灵开始,构建我们的Dinosaur Game示例。

  • 我们将添加块,以便在按下箭头键时它可以移动:

     when [up arrow v] key pressed
      change y by (10)
    
      when [down arrow v] key pressed
      change y by (-10)
    
      when [right arrow v] key pressed
      change x by (10)
    
      when [left arrow v] key pressed
      change x by (-10) 
    
  • 我们将添加另一个精灵,星星,并且当我们的恐龙触摸星星时,我们可以说我们赢得了游戏。

  • 让我们的恐龙在向上移动后检查它是否接触到了星星:

     when [up arrow v] key pressed
      change y by (10)
      if <touching (Star v) ?> then
      say (timer) for (2) seconds 
    
    • 回想一下,“if”块位于块的“控制”部分,而“接触?”块位于“感应”部分。

    • 我们还会添加一个“说”块以及“计时器”变量,这样我们的恐龙就可以告诉我们找到星星花了多长时间。

  • 但如果我们按下右键到达星星后,这种方法就不起作用了。

  • 我们希望我们的恐龙能够检查无论我们使用哪个键,我们是否已经到达了星星,因此我们需要在“if”块上右键单击或控制单击,并选择“复制”为每个键创建一个副本:

     when [up arrow v] key pressed
      change y by (10)
      if <touching (Star v) ?> then
      say (timer) for (2) seconds
    
      when [down arrow v] key pressed
      change y by (-10)
      if <touching (Star v) ?> then
      say (timer) for (2) seconds
    
      when [right arrow v] key pressed
      change x by (10)
      if <touching (Star v) ?> then
      say (timer) for (2) seconds
    
      when [left arrow v] key pressed
      change x by (-10)
      if <touching (Star v) ?> then
      say (timer) for (2) seconds 
    
  • 我们还会在我们的星星上添加一些代码,以便每次程序启动时它都会移动到随机位置:

     when green flag clicked
      go to (random position v) 
    
  • 现在,我们可以使用箭头键将我们的恐龙移动到星星,并且每次它都会告诉我们我们花了多长时间。

  • 看起来时间是以小数报告的,比如 9.70。因此,我们可以使用运算符部分中的“round”块将此时间四舍五入到最接近的秒数。

  • 但现在,我们必须将“圆形”块拖入每个方向的脚本中。

  • 当我们在项目中复制大量代码时,通常有一个更好的解决方案。

  • 在这种情况下,我们实际上可以创建一个新的自定义块,并在需要时引用它。

  • 我们将在“我的块”部分中查找,并点击“创建块”来创建一个新的块。我们将将其命名为“检查是否胜利”,因为我们正在尝试做的事情。然后,我们会看到这个块出现:

     define check if won 
    
    • 现在,我们可以在下面添加一些块,每次我们使用“检查是否胜利”块时,它都会运行。
  • 因此,我们将条件块和“说”块移动到它那里:

     define check if won
      if <touching (Star v) ?> then
      say (timer) for (2) seconds 
    
  • 对于我们的其他脚本,我们将拖出我们自己的“检查是否胜利”块:

     when [up arrow v] key pressed
      change y by (10)
      check if won
    
      when [down arrow v] key pressed
      change y by (-10)
      check if won
    
      when [right arrow v] key pressed
      change x by (10)
      check if won
    
      when [left arrow v] key pressed
      change x by (-10)
      check if won 
    
    • 现在,我们使用更少的块来实现相同的效果。
  • 如果我们想要四舍五入计时器的值,我们现在只需更改一个位置,而不是四个:

     define check if won
      if <touching (Star v) ?> then
      say (round(timer)) for (2) seconds 
    
  • 创建我们自己的块的能力将使我们能够改进项目的整体设计和可读性。

气球

  • 让我们为Balloon 1添加一个气球精灵,并添加使其充气和放气的块:

     when green flag clicked
      set size to (50) %
      repeat (10)
      change size by (10)
      end
      wait (1) seconds
      repeat (10)
      change size by (-10)
      end 
    
    • 我们的风筝将从一个 50%的大小开始,增加其大小 10 次,等待一秒钟,然后减小其大小 10 次(通过负 10 改变)。
  • 但这段代码需要其他人思考数字和循环在做什么,以理解会发生什么。我们可以通过添加更多块来使我们的代码更容易阅读。

  • 我们可以创建一个新的名为“inflate”的块,该块将使我们的风筝大小增加 10 次:

     define inflate
      repeat (10)
      change size by (10)
      end 
    
  • 我们还将创建另一个名为“deflate”的块,它将减小风筝的大小:

     define deflate
      repeat (10)
      change size by (-10)
      end 
    
  • 对于主脚本,我们将使用我们新的块:

     when green flag clicked
      set size to (50) %
      inflate
      wait (1) seconds
      deflate 
    
    • 注意,我们的程序仍然执行完全相同的事情,但“when flag clicked”下的脚本更容易阅读。
  • 我们可以将这些自定义块称为抽象,或者将更复杂的思想或动作取一个名字,这样我们就可以反复引用和使用。

  • 我们可以在Balloon 2中给自己更多的控制。让我们右键单击或按住控制键单击我们拥有的“inflate”块,并选择“编辑”。然后,我们将点击“添加输入”以使这个块能够接受一些输入,并将其命名为“n”:

    使用 inflate 和 n 作为输入创建一个块

    • “n”将是输入的名称,由于它将是一个数字,我们可以按照惯例使用“n”。
  • 我们还将点击“添加标签”,并将其更改为“times”,这样我们的块看起来就像这样:

    包含 inflate n times 块的代码

    • 标签让我们可以添加更多文字来描述块将要执行的操作。
  • 注意,我们的“inflate”的“define”块将显示一个椭圆形的“n”,我们可以在下面的块中使用它,所以我们将将其拖入“repeat”块中:

     define inflate (n) times
      repeat (n)
      change size by (10) 
    
    • 现在,“inflate”将根据“n”的次数增加风筝的大小 10 次。
  • 我们将以相同的方式对“deflate”进行操作。然后,在我们的“when flag clicked”脚本中,我们需要在每个自定义块中输入一个数字:

     when green flag clicked
      set size to (50) %
      inflate () times
      wait (1) seconds
      deflate () times 
    
    • 我们可以输入 10,这样风筝就会膨胀 10 次,或者我们可以将其更改为 20,或者任何其他值。
  • 通过让我们的函数能够接受输入,我们可以使它们更加灵活。

Walking Bear

  • 让我们看看一个在Walking Bear中可以接受多个输入的函数。

  • 我们将添加我们的熊角色,并将熊的四条腿服装重命名为“4”,两条腿的服装重命名为“2”。

  • 让我们创建一个新的名为“walk”的块,并给它一个名为“m”的输入。我们将添加一个标签“steps on”,然后另一个输入,“n”。最后,我们还将添加另一个标签,“feet”:

    带有 walk m steps on n feet 标签的块

    • 我们可以将输入命名为任何我们想要的,但我们将使用“m”和“n”。
  • 我们希望这个块执行的操作是让我们的熊在“n”只脚上走“m”步,所以我们将定义为:

     define walk (m) steps on (n) feet
      switch costume to (n)
      repeat (m)
      move (1) steps 
    
    • 我们首先将我们的服装切换到“n”,然后移动 1 步“m”次。
  • 现在,在我们的主脚本中,我们可以告诉我们的熊走任意数量的步数,用两只或四只脚:

     when green flag clicked
      walk (30) steps on (4) feet
      walk (30) steps on (2) feet 
    
    • 注意,当点击标志时,更容易理解我们的熊会做什么。而且我们也避免了反复重复多个代码块。
  • 通过我们自己的自定义代码块,我们可以定义可以重用并使我们的项目更有条理的复杂行为。

posted @ 2025-11-08 11:25  绝不原创的飞龙  阅读(2)  评论(0)    收藏  举报